michael@0: /* michael@0: ******************************************************************************* michael@0: * Copyright (C) 1997-2013, International Business Machines Corporation and * michael@0: * others. All Rights Reserved. * michael@0: ******************************************************************************* michael@0: * michael@0: * File DECIMFMT.CPP michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 02/19/97 aliu Converted from java. michael@0: * 03/20/97 clhuang Implemented with new APIs. michael@0: * 03/31/97 aliu Moved isLONG_MIN to DigitList, and fixed it. michael@0: * 04/3/97 aliu Rewrote parsing and formatting completely, and michael@0: * cleaned up and debugged. Actually works now. michael@0: * Implemented NAN and INF handling, for both parsing michael@0: * and formatting. Extensive testing & debugging. michael@0: * 04/10/97 aliu Modified to compile on AIX. michael@0: * 04/16/97 aliu Rewrote to use DigitList, which has been resurrected. michael@0: * Changed DigitCount to int per code review. michael@0: * 07/09/97 helena Made ParsePosition into a class. michael@0: * 08/26/97 aliu Extensive changes to applyPattern; completely michael@0: * rewritten from the Java. michael@0: * 09/09/97 aliu Ported over support for exponential formats. michael@0: * 07/20/98 stephen JDK 1.2 sync up. michael@0: * Various instances of '0' replaced with 'NULL' michael@0: * Check for grouping size in subFormat() michael@0: * Brought subParse() in line with Java 1.2 michael@0: * Added method appendAffix() michael@0: * 08/24/1998 srl Removed Mutex calls. This is not a thread safe class! michael@0: * 02/22/99 stephen Removed character literals for EBCDIC safety michael@0: * 06/24/99 helena Integrated Alan's NF enhancements and Java2 bug fixes michael@0: * 06/28/99 stephen Fixed bugs in toPattern(). michael@0: * 06/29/99 stephen Fixed operator= to copy fFormatWidth, fPad, michael@0: * fPadPosition michael@0: ******************************************************************************** michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: michael@0: #include "fphdlimp.h" michael@0: #include "unicode/decimfmt.h" michael@0: #include "unicode/choicfmt.h" michael@0: #include "unicode/ucurr.h" michael@0: #include "unicode/ustring.h" michael@0: #include "unicode/dcfmtsym.h" michael@0: #include "unicode/ures.h" michael@0: #include "unicode/uchar.h" michael@0: #include "unicode/uniset.h" michael@0: #include "unicode/curramt.h" michael@0: #include "unicode/currpinf.h" michael@0: #include "unicode/plurrule.h" michael@0: #include "unicode/utf16.h" michael@0: #include "unicode/numsys.h" michael@0: #include "unicode/localpointer.h" michael@0: #include "uresimp.h" michael@0: #include "ucurrimp.h" michael@0: #include "charstr.h" michael@0: #include "cmemory.h" michael@0: #include "patternprops.h" michael@0: #include "digitlst.h" michael@0: #include "cstring.h" michael@0: #include "umutex.h" michael@0: #include "uassert.h" michael@0: #include "putilimp.h" michael@0: #include michael@0: #include "hash.h" michael@0: #include "decfmtst.h" michael@0: #include "dcfmtimp.h" michael@0: #include "plurrule_impl.h" michael@0: michael@0: /* michael@0: * On certain platforms, round is a macro defined in math.h michael@0: * This undefine is to avoid conflict between the macro and michael@0: * the function defined below. michael@0: */ michael@0: #ifdef round michael@0: #undef round michael@0: #endif michael@0: michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: #ifdef FMT_DEBUG michael@0: #include michael@0: static void _debugout(const char *f, int l, const UnicodeString& s) { michael@0: char buf[2000]; michael@0: s.extract((int32_t) 0, s.length(), buf, "utf-8"); michael@0: printf("%s:%d: %s\n", f,l, buf); michael@0: } michael@0: #define debugout(x) _debugout(__FILE__,__LINE__,x) michael@0: #define debug(x) printf("%s:%d: %s\n", __FILE__,__LINE__, x); michael@0: static const UnicodeString dbg_null("",""); michael@0: #define DEREFSTR(x) ((x!=NULL)?(*x):(dbg_null)) michael@0: #else michael@0: #define debugout(x) michael@0: #define debug(x) michael@0: #endif michael@0: michael@0: michael@0: michael@0: /* == Fastpath calculation. == michael@0: */ michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: inline DecimalFormatInternal& internalData(uint8_t *reserved) { michael@0: return *reinterpret_cast(reserved); michael@0: } michael@0: inline const DecimalFormatInternal& internalData(const uint8_t *reserved) { michael@0: return *reinterpret_cast(reserved); michael@0: } michael@0: #else michael@0: #endif michael@0: michael@0: /* For currency parsing purose, michael@0: * Need to remember all prefix patterns and suffix patterns of michael@0: * every currency format pattern, michael@0: * including the pattern of default currecny style michael@0: * and plural currency style. And the patterns are set through applyPattern. michael@0: */ michael@0: struct AffixPatternsForCurrency : public UMemory { michael@0: // negative prefix pattern michael@0: UnicodeString negPrefixPatternForCurrency; michael@0: // negative suffix pattern michael@0: UnicodeString negSuffixPatternForCurrency; michael@0: // positive prefix pattern michael@0: UnicodeString posPrefixPatternForCurrency; michael@0: // positive suffix pattern michael@0: UnicodeString posSuffixPatternForCurrency; michael@0: int8_t patternType; michael@0: michael@0: AffixPatternsForCurrency(const UnicodeString& negPrefix, michael@0: const UnicodeString& negSuffix, michael@0: const UnicodeString& posPrefix, michael@0: const UnicodeString& posSuffix, michael@0: int8_t type) { michael@0: negPrefixPatternForCurrency = negPrefix; michael@0: negSuffixPatternForCurrency = negSuffix; michael@0: posPrefixPatternForCurrency = posPrefix; michael@0: posSuffixPatternForCurrency = posSuffix; michael@0: patternType = type; michael@0: } michael@0: #ifdef FMT_DEBUG michael@0: void dump() const { michael@0: debugout( UnicodeString("AffixPatternsForCurrency( -=\"") + michael@0: negPrefixPatternForCurrency + (UnicodeString)"\"/\"" + michael@0: negSuffixPatternForCurrency + (UnicodeString)"\" +=\"" + michael@0: posPrefixPatternForCurrency + (UnicodeString)"\"/\"" + michael@0: posSuffixPatternForCurrency + (UnicodeString)"\" )"); michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: /* affix for currency formatting when the currency sign in the pattern michael@0: * equals to 3, such as the pattern contains 3 currency sign or michael@0: * the formatter style is currency plural format style. michael@0: */ michael@0: struct AffixesForCurrency : public UMemory { michael@0: // negative prefix michael@0: UnicodeString negPrefixForCurrency; michael@0: // negative suffix michael@0: UnicodeString negSuffixForCurrency; michael@0: // positive prefix michael@0: UnicodeString posPrefixForCurrency; michael@0: // positive suffix michael@0: UnicodeString posSuffixForCurrency; michael@0: michael@0: int32_t formatWidth; michael@0: michael@0: AffixesForCurrency(const UnicodeString& negPrefix, michael@0: const UnicodeString& negSuffix, michael@0: const UnicodeString& posPrefix, michael@0: const UnicodeString& posSuffix) { michael@0: negPrefixForCurrency = negPrefix; michael@0: negSuffixForCurrency = negSuffix; michael@0: posPrefixForCurrency = posPrefix; michael@0: posSuffixForCurrency = posSuffix; michael@0: } michael@0: #ifdef FMT_DEBUG michael@0: void dump() const { michael@0: debugout( UnicodeString("AffixesForCurrency( -=\"") + michael@0: negPrefixForCurrency + (UnicodeString)"\"/\"" + michael@0: negSuffixForCurrency + (UnicodeString)"\" +=\"" + michael@0: posPrefixForCurrency + (UnicodeString)"\"/\"" + michael@0: posSuffixForCurrency + (UnicodeString)"\" )"); michael@0: } michael@0: #endif michael@0: }; michael@0: michael@0: U_CDECL_BEGIN michael@0: michael@0: /** michael@0: * @internal ICU 4.2 michael@0: */ michael@0: static UBool U_CALLCONV decimfmtAffixValueComparator(UHashTok val1, UHashTok val2); michael@0: michael@0: /** michael@0: * @internal ICU 4.2 michael@0: */ michael@0: static UBool U_CALLCONV decimfmtAffixPatternValueComparator(UHashTok val1, UHashTok val2); michael@0: michael@0: michael@0: static UBool michael@0: U_CALLCONV decimfmtAffixValueComparator(UHashTok val1, UHashTok val2) { michael@0: const AffixesForCurrency* affix_1 = michael@0: (AffixesForCurrency*)val1.pointer; michael@0: const AffixesForCurrency* affix_2 = michael@0: (AffixesForCurrency*)val2.pointer; michael@0: return affix_1->negPrefixForCurrency == affix_2->negPrefixForCurrency && michael@0: affix_1->negSuffixForCurrency == affix_2->negSuffixForCurrency && michael@0: affix_1->posPrefixForCurrency == affix_2->posPrefixForCurrency && michael@0: affix_1->posSuffixForCurrency == affix_2->posSuffixForCurrency; michael@0: } michael@0: michael@0: michael@0: static UBool michael@0: U_CALLCONV decimfmtAffixPatternValueComparator(UHashTok val1, UHashTok val2) { michael@0: const AffixPatternsForCurrency* affix_1 = michael@0: (AffixPatternsForCurrency*)val1.pointer; michael@0: const AffixPatternsForCurrency* affix_2 = michael@0: (AffixPatternsForCurrency*)val2.pointer; michael@0: return affix_1->negPrefixPatternForCurrency == michael@0: affix_2->negPrefixPatternForCurrency && michael@0: affix_1->negSuffixPatternForCurrency == michael@0: affix_2->negSuffixPatternForCurrency && michael@0: affix_1->posPrefixPatternForCurrency == michael@0: affix_2->posPrefixPatternForCurrency && michael@0: affix_1->posSuffixPatternForCurrency == michael@0: affix_2->posSuffixPatternForCurrency && michael@0: affix_1->patternType == affix_2->patternType; michael@0: } michael@0: michael@0: U_CDECL_END michael@0: michael@0: michael@0: michael@0: michael@0: // ***************************************************************************** michael@0: // class DecimalFormat michael@0: // ***************************************************************************** michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat) michael@0: michael@0: // Constants for characters used in programmatic (unlocalized) patterns. michael@0: #define kPatternZeroDigit ((UChar)0x0030) /*'0'*/ michael@0: #define kPatternSignificantDigit ((UChar)0x0040) /*'@'*/ michael@0: #define kPatternGroupingSeparator ((UChar)0x002C) /*','*/ michael@0: #define kPatternDecimalSeparator ((UChar)0x002E) /*'.'*/ michael@0: #define kPatternPerMill ((UChar)0x2030) michael@0: #define kPatternPercent ((UChar)0x0025) /*'%'*/ michael@0: #define kPatternDigit ((UChar)0x0023) /*'#'*/ michael@0: #define kPatternSeparator ((UChar)0x003B) /*';'*/ michael@0: #define kPatternExponent ((UChar)0x0045) /*'E'*/ michael@0: #define kPatternPlus ((UChar)0x002B) /*'+'*/ michael@0: #define kPatternMinus ((UChar)0x002D) /*'-'*/ michael@0: #define kPatternPadEscape ((UChar)0x002A) /*'*'*/ michael@0: #define kQuote ((UChar)0x0027) /*'\''*/ michael@0: /** michael@0: * The CURRENCY_SIGN is the standard Unicode symbol for currency. It michael@0: * is used in patterns and substitued with either the currency symbol, michael@0: * or if it is doubled, with the international currency symbol. If the michael@0: * CURRENCY_SIGN is seen in a pattern, then the decimal separator is michael@0: * replaced with the monetary decimal separator. michael@0: */ michael@0: #define kCurrencySign ((UChar)0x00A4) michael@0: #define kDefaultPad ((UChar)0x0020) /* */ michael@0: michael@0: const int32_t DecimalFormat::kDoubleIntegerDigits = 309; michael@0: const int32_t DecimalFormat::kDoubleFractionDigits = 340; michael@0: michael@0: const int32_t DecimalFormat::kMaxScientificIntegerDigits = 8; michael@0: michael@0: /** michael@0: * These are the tags we expect to see in normal resource bundle files associated michael@0: * with a locale. michael@0: */ michael@0: const char DecimalFormat::fgNumberPatterns[]="NumberPatterns"; // Deprecated - not used michael@0: static const char fgNumberElements[]="NumberElements"; michael@0: static const char fgLatn[]="latn"; michael@0: static const char fgPatterns[]="patterns"; michael@0: static const char fgDecimalFormat[]="decimalFormat"; michael@0: static const char fgCurrencyFormat[]="currencyFormat"; michael@0: michael@0: static const UChar fgTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0}; michael@0: michael@0: inline int32_t _min(int32_t a, int32_t b) { return (agetName(), resource, &status); michael@0: resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &status); michael@0: const UChar *resStr = ures_getStringByKeyWithFallback(resource, fgDecimalFormat, &len, &status); michael@0: if ( status == U_MISSING_RESOURCE_ERROR && uprv_strcmp(fgLatn,ns->getName())) { michael@0: status = U_ZERO_ERROR; michael@0: resource = ures_getByKeyWithFallback(top, fgNumberElements, resource, &status); michael@0: resource = ures_getByKeyWithFallback(resource, fgLatn, resource, &status); michael@0: resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &status); michael@0: resStr = ures_getStringByKeyWithFallback(resource, fgDecimalFormat, &len, &status); michael@0: } michael@0: str.setTo(TRUE, resStr, len); michael@0: pattern = &str; michael@0: ures_close(resource); michael@0: ures_close(top); michael@0: } michael@0: michael@0: delete ns; michael@0: michael@0: if (U_FAILURE(status)) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: if (pattern->indexOf((UChar)kCurrencySign) >= 0) { michael@0: // If it looks like we are going to use a currency pattern michael@0: // then do the time consuming lookup. michael@0: setCurrencyForSymbols(); michael@0: } else { michael@0: setCurrencyInternally(NULL, status); michael@0: } michael@0: michael@0: const UnicodeString* patternUsed; michael@0: UnicodeString currencyPluralPatternForOther; michael@0: // apply pattern michael@0: if (fStyle == UNUM_CURRENCY_PLURAL) { michael@0: fCurrencyPluralInfo = new CurrencyPluralInfo(fSymbols->getLocale(), status); michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: // the pattern used in format is not fixed until formatting, michael@0: // in which, the number is known and michael@0: // will be used to pick the right pattern based on plural count. michael@0: // Here, set the pattern as the pattern of plural count == "other". michael@0: // For most locale, the patterns are probably the same for all michael@0: // plural count. If not, the right pattern need to be re-applied michael@0: // during format. michael@0: fCurrencyPluralInfo->getCurrencyPluralPattern(UNICODE_STRING("other", 5), currencyPluralPatternForOther); michael@0: patternUsed = ¤cyPluralPatternForOther; michael@0: // TODO: not needed? michael@0: setCurrencyForSymbols(); michael@0: michael@0: } else { michael@0: patternUsed = pattern; michael@0: } michael@0: michael@0: if (patternUsed->indexOf(kCurrencySign) != -1) { michael@0: // initialize for currency, not only for plural format, michael@0: // but also for mix parsing michael@0: if (fCurrencyPluralInfo == NULL) { michael@0: fCurrencyPluralInfo = new CurrencyPluralInfo(fSymbols->getLocale(), status); michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: } michael@0: // need it for mix parsing michael@0: setupCurrencyAffixPatterns(status); michael@0: // expanded affixes for plural names michael@0: if (patternUsed->indexOf(fgTripleCurrencySign, 3, 0) != -1) { michael@0: setupCurrencyAffixes(*patternUsed, TRUE, TRUE, status); michael@0: } michael@0: } michael@0: michael@0: applyPatternWithoutExpandAffix(*patternUsed,FALSE, parseErr, status); michael@0: michael@0: // expand affixes michael@0: if (fCurrencySignCount != fgCurrencySignCountInPluralFormat) { michael@0: expandAffixAdjustWidth(NULL); michael@0: } michael@0: michael@0: // If it was a currency format, apply the appropriate rounding by michael@0: // resetting the currency. NOTE: this copies fCurrency on top of itself. michael@0: if (fCurrencySignCount != fgCurrencySignCountZero) { michael@0: setCurrencyInternally(getCurrency(), status); michael@0: } michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: DecimalFormatInternal &data = internalData(fReserved); michael@0: data.fFastFormatStatus = kFastpathNO; // allow it to be calculated michael@0: data.fFastParseStatus = kFastpathNO; // allow it to be calculated michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: michael@0: void michael@0: DecimalFormat::setupCurrencyAffixPatterns(UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: UParseError parseErr; michael@0: fAffixPatternsForCurrency = initHashForAffixPattern(status); michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: NumberingSystem *ns = NumberingSystem::createInstance(fSymbols->getLocale(),status); michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: // Save the default currency patterns of this locale. michael@0: // Here, chose onlyApplyPatternWithoutExpandAffix without michael@0: // expanding the affix patterns into affixes. michael@0: UnicodeString currencyPattern; michael@0: UErrorCode error = U_ZERO_ERROR; michael@0: michael@0: UResourceBundle *resource = ures_open(NULL, fSymbols->getLocale().getName(), &error); michael@0: UResourceBundle *numElements = ures_getByKeyWithFallback(resource, fgNumberElements, NULL, &error); michael@0: resource = ures_getByKeyWithFallback(numElements, ns->getName(), resource, &error); michael@0: resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &error); michael@0: int32_t patLen = 0; michael@0: const UChar *patResStr = ures_getStringByKeyWithFallback(resource, fgCurrencyFormat, &patLen, &error); michael@0: if ( error == U_MISSING_RESOURCE_ERROR && uprv_strcmp(ns->getName(),fgLatn)) { michael@0: error = U_ZERO_ERROR; michael@0: resource = ures_getByKeyWithFallback(numElements, fgLatn, resource, &error); michael@0: resource = ures_getByKeyWithFallback(resource, fgPatterns, resource, &error); michael@0: patResStr = ures_getStringByKeyWithFallback(resource, fgCurrencyFormat, &patLen, &error); michael@0: } michael@0: ures_close(numElements); michael@0: ures_close(resource); michael@0: delete ns; michael@0: michael@0: if (U_SUCCESS(error)) { michael@0: applyPatternWithoutExpandAffix(UnicodeString(patResStr, patLen), false, michael@0: parseErr, status); michael@0: AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( michael@0: *fNegPrefixPattern, michael@0: *fNegSuffixPattern, michael@0: *fPosPrefixPattern, michael@0: *fPosSuffixPattern, michael@0: UCURR_SYMBOL_NAME); michael@0: fAffixPatternsForCurrency->put(UNICODE_STRING("default", 7), affixPtn, status); michael@0: } michael@0: michael@0: // save the unique currency plural patterns of this locale. michael@0: Hashtable* pluralPtn = fCurrencyPluralInfo->fPluralCountToCurrencyUnitPattern; michael@0: const UHashElement* element = NULL; michael@0: int32_t pos = -1; michael@0: Hashtable pluralPatternSet; michael@0: while ((element = pluralPtn->nextElement(pos)) != NULL) { michael@0: const UHashTok valueTok = element->value; michael@0: const UnicodeString* value = (UnicodeString*)valueTok.pointer; michael@0: const UHashTok keyTok = element->key; michael@0: const UnicodeString* key = (UnicodeString*)keyTok.pointer; michael@0: if (pluralPatternSet.geti(*value) != 1) { michael@0: pluralPatternSet.puti(*value, 1, status); michael@0: applyPatternWithoutExpandAffix(*value, false, parseErr, status); michael@0: AffixPatternsForCurrency* affixPtn = new AffixPatternsForCurrency( michael@0: *fNegPrefixPattern, michael@0: *fNegSuffixPattern, michael@0: *fPosPrefixPattern, michael@0: *fPosSuffixPattern, michael@0: UCURR_LONG_NAME); michael@0: fAffixPatternsForCurrency->put(*key, affixPtn, status); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: void michael@0: DecimalFormat::setupCurrencyAffixes(const UnicodeString& pattern, michael@0: UBool setupForCurrentPattern, michael@0: UBool setupForPluralPattern, michael@0: UErrorCode& status) { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: UParseError parseErr; michael@0: if (setupForCurrentPattern) { michael@0: if (fAffixesForCurrency) { michael@0: deleteHashForAffix(fAffixesForCurrency); michael@0: } michael@0: fAffixesForCurrency = initHashForAffix(status); michael@0: if (U_SUCCESS(status)) { michael@0: applyPatternWithoutExpandAffix(pattern, false, parseErr, status); michael@0: const PluralRules* pluralRules = fCurrencyPluralInfo->getPluralRules(); michael@0: StringEnumeration* keywords = pluralRules->getKeywords(status); michael@0: if (U_SUCCESS(status)) { michael@0: const UnicodeString* pluralCount; michael@0: while ((pluralCount = keywords->snext(status)) != NULL) { michael@0: if ( U_SUCCESS(status) ) { michael@0: expandAffixAdjustWidth(pluralCount); michael@0: AffixesForCurrency* affix = new AffixesForCurrency( michael@0: fNegativePrefix, fNegativeSuffix, fPositivePrefix, fPositiveSuffix); michael@0: fAffixesForCurrency->put(*pluralCount, affix, status); michael@0: } michael@0: } michael@0: } michael@0: delete keywords; michael@0: } michael@0: } michael@0: michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: if (setupForPluralPattern) { michael@0: if (fPluralAffixesForCurrency) { michael@0: deleteHashForAffix(fPluralAffixesForCurrency); michael@0: } michael@0: fPluralAffixesForCurrency = initHashForAffix(status); michael@0: if (U_SUCCESS(status)) { michael@0: const PluralRules* pluralRules = fCurrencyPluralInfo->getPluralRules(); michael@0: StringEnumeration* keywords = pluralRules->getKeywords(status); michael@0: if (U_SUCCESS(status)) { michael@0: const UnicodeString* pluralCount; michael@0: while ((pluralCount = keywords->snext(status)) != NULL) { michael@0: if ( U_SUCCESS(status) ) { michael@0: UnicodeString ptn; michael@0: fCurrencyPluralInfo->getCurrencyPluralPattern(*pluralCount, ptn); michael@0: applyPatternInternally(*pluralCount, ptn, false, parseErr, status); michael@0: AffixesForCurrency* affix = new AffixesForCurrency( michael@0: fNegativePrefix, fNegativeSuffix, fPositivePrefix, fPositiveSuffix); michael@0: fPluralAffixesForCurrency->put(*pluralCount, affix, status); michael@0: } michael@0: } michael@0: } michael@0: delete keywords; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: DecimalFormat::~DecimalFormat() michael@0: { michael@0: delete fPosPrefixPattern; michael@0: delete fPosSuffixPattern; michael@0: delete fNegPrefixPattern; michael@0: delete fNegSuffixPattern; michael@0: delete fCurrencyChoice; michael@0: delete fMultiplier; michael@0: delete fSymbols; michael@0: delete fRoundingIncrement; michael@0: deleteHashForAffixPattern(); michael@0: deleteHashForAffix(fAffixesForCurrency); michael@0: deleteHashForAffix(fPluralAffixesForCurrency); michael@0: delete fCurrencyPluralInfo; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // copy constructor michael@0: michael@0: DecimalFormat::DecimalFormat(const DecimalFormat &source) : michael@0: NumberFormat(source) { michael@0: init(); michael@0: *this = source; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // assignment operator michael@0: michael@0: template michael@0: static void _copy_ptr(T** pdest, const T* source) { michael@0: if (source == NULL) { michael@0: delete *pdest; michael@0: *pdest = NULL; michael@0: } else if (*pdest == NULL) { michael@0: *pdest = new T(*source); michael@0: } else { michael@0: **pdest = *source; michael@0: } michael@0: } michael@0: michael@0: template michael@0: static void _clone_ptr(T** pdest, const T* source) { michael@0: delete *pdest; michael@0: if (source == NULL) { michael@0: *pdest = NULL; michael@0: } else { michael@0: *pdest = static_cast(source->clone()); michael@0: } michael@0: } michael@0: michael@0: DecimalFormat& michael@0: DecimalFormat::operator=(const DecimalFormat& rhs) michael@0: { michael@0: if(this != &rhs) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: NumberFormat::operator=(rhs); michael@0: fStaticSets = DecimalFormatStaticSets::getStaticSets(status); michael@0: fPositivePrefix = rhs.fPositivePrefix; michael@0: fPositiveSuffix = rhs.fPositiveSuffix; michael@0: fNegativePrefix = rhs.fNegativePrefix; michael@0: fNegativeSuffix = rhs.fNegativeSuffix; michael@0: _copy_ptr(&fPosPrefixPattern, rhs.fPosPrefixPattern); michael@0: _copy_ptr(&fPosSuffixPattern, rhs.fPosSuffixPattern); michael@0: _copy_ptr(&fNegPrefixPattern, rhs.fNegPrefixPattern); michael@0: _copy_ptr(&fNegSuffixPattern, rhs.fNegSuffixPattern); michael@0: _clone_ptr(&fCurrencyChoice, rhs.fCurrencyChoice); michael@0: setRoundingIncrement(rhs.getRoundingIncrement()); michael@0: fRoundingMode = rhs.fRoundingMode; michael@0: setMultiplier(rhs.getMultiplier()); michael@0: fGroupingSize = rhs.fGroupingSize; michael@0: fGroupingSize2 = rhs.fGroupingSize2; michael@0: fDecimalSeparatorAlwaysShown = rhs.fDecimalSeparatorAlwaysShown; michael@0: _copy_ptr(&fSymbols, rhs.fSymbols); michael@0: fUseExponentialNotation = rhs.fUseExponentialNotation; michael@0: fExponentSignAlwaysShown = rhs.fExponentSignAlwaysShown; michael@0: fBoolFlags = rhs.fBoolFlags; michael@0: /*Bertrand A. D. Update 98.03.17*/ michael@0: fCurrencySignCount = rhs.fCurrencySignCount; michael@0: /*end of Update*/ michael@0: fMinExponentDigits = rhs.fMinExponentDigits; michael@0: michael@0: /* sfb 990629 */ michael@0: fFormatWidth = rhs.fFormatWidth; michael@0: fPad = rhs.fPad; michael@0: fPadPosition = rhs.fPadPosition; michael@0: /* end sfb */ michael@0: fMinSignificantDigits = rhs.fMinSignificantDigits; michael@0: fMaxSignificantDigits = rhs.fMaxSignificantDigits; michael@0: fUseSignificantDigits = rhs.fUseSignificantDigits; michael@0: fFormatPattern = rhs.fFormatPattern; michael@0: fStyle = rhs.fStyle; michael@0: fCurrencySignCount = rhs.fCurrencySignCount; michael@0: _clone_ptr(&fCurrencyPluralInfo, rhs.fCurrencyPluralInfo); michael@0: deleteHashForAffixPattern(); michael@0: if (rhs.fAffixPatternsForCurrency) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: fAffixPatternsForCurrency = initHashForAffixPattern(status); michael@0: copyHashForAffixPattern(rhs.fAffixPatternsForCurrency, michael@0: fAffixPatternsForCurrency, status); michael@0: } michael@0: deleteHashForAffix(fAffixesForCurrency); michael@0: if (rhs.fAffixesForCurrency) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: fAffixesForCurrency = initHashForAffixPattern(status); michael@0: copyHashForAffix(rhs.fAffixesForCurrency, fAffixesForCurrency, status); michael@0: } michael@0: deleteHashForAffix(fPluralAffixesForCurrency); michael@0: if (rhs.fPluralAffixesForCurrency) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: fPluralAffixesForCurrency = initHashForAffixPattern(status); michael@0: copyHashForAffix(rhs.fPluralAffixesForCurrency, fPluralAffixesForCurrency, status); michael@0: } michael@0: } michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: return *this; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: UBool michael@0: DecimalFormat::operator==(const Format& that) const michael@0: { michael@0: if (this == &that) michael@0: return TRUE; michael@0: michael@0: // NumberFormat::operator== guarantees this cast is safe michael@0: const DecimalFormat* other = (DecimalFormat*)&that; michael@0: michael@0: #ifdef FMT_DEBUG michael@0: // This code makes it easy to determine why two format objects that should michael@0: // be equal aren't. michael@0: UBool first = TRUE; michael@0: if (!NumberFormat::operator==(that)) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("NumberFormat::!="); michael@0: } else { michael@0: if (!((fPosPrefixPattern == other->fPosPrefixPattern && // both null michael@0: fPositivePrefix == other->fPositivePrefix) michael@0: || (fPosPrefixPattern != 0 && other->fPosPrefixPattern != 0 && michael@0: *fPosPrefixPattern == *other->fPosPrefixPattern))) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Pos Prefix !="); michael@0: } michael@0: if (!((fPosSuffixPattern == other->fPosSuffixPattern && // both null michael@0: fPositiveSuffix == other->fPositiveSuffix) michael@0: || (fPosSuffixPattern != 0 && other->fPosSuffixPattern != 0 && michael@0: *fPosSuffixPattern == *other->fPosSuffixPattern))) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Pos Suffix !="); michael@0: } michael@0: if (!((fNegPrefixPattern == other->fNegPrefixPattern && // both null michael@0: fNegativePrefix == other->fNegativePrefix) michael@0: || (fNegPrefixPattern != 0 && other->fNegPrefixPattern != 0 && michael@0: *fNegPrefixPattern == *other->fNegPrefixPattern))) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Neg Prefix "); michael@0: if (fNegPrefixPattern == NULL) { michael@0: debug("NULL("); michael@0: debugout(fNegativePrefix); michael@0: debug(")"); michael@0: } else { michael@0: debugout(*fNegPrefixPattern); michael@0: } michael@0: debug(" != "); michael@0: if (other->fNegPrefixPattern == NULL) { michael@0: debug("NULL("); michael@0: debugout(other->fNegativePrefix); michael@0: debug(")"); michael@0: } else { michael@0: debugout(*other->fNegPrefixPattern); michael@0: } michael@0: } michael@0: if (!((fNegSuffixPattern == other->fNegSuffixPattern && // both null michael@0: fNegativeSuffix == other->fNegativeSuffix) michael@0: || (fNegSuffixPattern != 0 && other->fNegSuffixPattern != 0 && michael@0: *fNegSuffixPattern == *other->fNegSuffixPattern))) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Neg Suffix "); michael@0: if (fNegSuffixPattern == NULL) { michael@0: debug("NULL("); michael@0: debugout(fNegativeSuffix); michael@0: debug(")"); michael@0: } else { michael@0: debugout(*fNegSuffixPattern); michael@0: } michael@0: debug(" != "); michael@0: if (other->fNegSuffixPattern == NULL) { michael@0: debug("NULL("); michael@0: debugout(other->fNegativeSuffix); michael@0: debug(")"); michael@0: } else { michael@0: debugout(*other->fNegSuffixPattern); michael@0: } michael@0: } michael@0: if (!((fRoundingIncrement == other->fRoundingIncrement) // both null michael@0: || (fRoundingIncrement != NULL && michael@0: other->fRoundingIncrement != NULL && michael@0: *fRoundingIncrement == *other->fRoundingIncrement))) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Rounding Increment !="); michael@0: } michael@0: if (getMultiplier() != other->getMultiplier()) { michael@0: if (first) { printf("[ "); first = FALSE; } michael@0: printf("Multiplier %ld != %ld", getMultiplier(), other->getMultiplier()); michael@0: } michael@0: if (fGroupingSize != other->fGroupingSize) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: printf("Grouping Size %ld != %ld", fGroupingSize, other->fGroupingSize); michael@0: } michael@0: if (fGroupingSize2 != other->fGroupingSize2) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: printf("Secondary Grouping Size %ld != %ld", fGroupingSize2, other->fGroupingSize2); michael@0: } michael@0: if (fDecimalSeparatorAlwaysShown != other->fDecimalSeparatorAlwaysShown) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: printf("Dec Sep Always %d != %d", fDecimalSeparatorAlwaysShown, other->fDecimalSeparatorAlwaysShown); michael@0: } michael@0: if (fUseExponentialNotation != other->fUseExponentialNotation) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Use Exp !="); michael@0: } michael@0: if (!(!fUseExponentialNotation || michael@0: fMinExponentDigits != other->fMinExponentDigits)) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Exp Digits !="); michael@0: } michael@0: if (*fSymbols != *(other->fSymbols)) { michael@0: if (first) { printf("[ "); first = FALSE; } else { printf(", "); } michael@0: debug("Symbols !="); michael@0: } michael@0: // TODO Add debug stuff for significant digits here michael@0: if (fUseSignificantDigits != other->fUseSignificantDigits) { michael@0: debug("fUseSignificantDigits !="); michael@0: } michael@0: if (fUseSignificantDigits && michael@0: fMinSignificantDigits != other->fMinSignificantDigits) { michael@0: debug("fMinSignificantDigits !="); michael@0: } michael@0: if (fUseSignificantDigits && michael@0: fMaxSignificantDigits != other->fMaxSignificantDigits) { michael@0: debug("fMaxSignificantDigits !="); michael@0: } michael@0: michael@0: if (!first) { printf(" ]"); } michael@0: if (fCurrencySignCount != other->fCurrencySignCount) { michael@0: debug("fCurrencySignCount !="); michael@0: } michael@0: if (fCurrencyPluralInfo == other->fCurrencyPluralInfo) { michael@0: debug("fCurrencyPluralInfo == "); michael@0: if (fCurrencyPluralInfo == NULL) { michael@0: debug("fCurrencyPluralInfo == NULL"); michael@0: } michael@0: } michael@0: if (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo != NULL && michael@0: *fCurrencyPluralInfo != *(other->fCurrencyPluralInfo)) { michael@0: debug("fCurrencyPluralInfo !="); michael@0: } michael@0: if (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo == NULL || michael@0: fCurrencyPluralInfo == NULL && other->fCurrencyPluralInfo != NULL) { michael@0: debug("fCurrencyPluralInfo one NULL, the other not"); michael@0: } michael@0: if (fCurrencyPluralInfo == NULL && other->fCurrencyPluralInfo == NULL) { michael@0: debug("fCurrencyPluralInfo == "); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: return (NumberFormat::operator==(that) && michael@0: ((fCurrencySignCount == fgCurrencySignCountInPluralFormat) ? michael@0: (fAffixPatternsForCurrency->equals(*other->fAffixPatternsForCurrency)) : michael@0: (((fPosPrefixPattern == other->fPosPrefixPattern && // both null michael@0: fPositivePrefix == other->fPositivePrefix) michael@0: || (fPosPrefixPattern != 0 && other->fPosPrefixPattern != 0 && michael@0: *fPosPrefixPattern == *other->fPosPrefixPattern)) && michael@0: ((fPosSuffixPattern == other->fPosSuffixPattern && // both null michael@0: fPositiveSuffix == other->fPositiveSuffix) michael@0: || (fPosSuffixPattern != 0 && other->fPosSuffixPattern != 0 && michael@0: *fPosSuffixPattern == *other->fPosSuffixPattern)) && michael@0: ((fNegPrefixPattern == other->fNegPrefixPattern && // both null michael@0: fNegativePrefix == other->fNegativePrefix) michael@0: || (fNegPrefixPattern != 0 && other->fNegPrefixPattern != 0 && michael@0: *fNegPrefixPattern == *other->fNegPrefixPattern)) && michael@0: ((fNegSuffixPattern == other->fNegSuffixPattern && // both null michael@0: fNegativeSuffix == other->fNegativeSuffix) michael@0: || (fNegSuffixPattern != 0 && other->fNegSuffixPattern != 0 && michael@0: *fNegSuffixPattern == *other->fNegSuffixPattern)))) && michael@0: ((fRoundingIncrement == other->fRoundingIncrement) // both null michael@0: || (fRoundingIncrement != NULL && michael@0: other->fRoundingIncrement != NULL && michael@0: *fRoundingIncrement == *other->fRoundingIncrement)) && michael@0: getMultiplier() == other->getMultiplier() && michael@0: fGroupingSize == other->fGroupingSize && michael@0: fGroupingSize2 == other->fGroupingSize2 && michael@0: fDecimalSeparatorAlwaysShown == other->fDecimalSeparatorAlwaysShown && michael@0: fUseExponentialNotation == other->fUseExponentialNotation && michael@0: (!fUseExponentialNotation || michael@0: fMinExponentDigits == other->fMinExponentDigits) && michael@0: *fSymbols == *(other->fSymbols) && michael@0: fUseSignificantDigits == other->fUseSignificantDigits && michael@0: (!fUseSignificantDigits || michael@0: (fMinSignificantDigits == other->fMinSignificantDigits && michael@0: fMaxSignificantDigits == other->fMaxSignificantDigits)) && michael@0: fCurrencySignCount == other->fCurrencySignCount && michael@0: ((fCurrencyPluralInfo == other->fCurrencyPluralInfo && michael@0: fCurrencyPluralInfo == NULL) || michael@0: (fCurrencyPluralInfo != NULL && other->fCurrencyPluralInfo != NULL && michael@0: *fCurrencyPluralInfo == *(other->fCurrencyPluralInfo)))); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: Format* michael@0: DecimalFormat::clone() const michael@0: { michael@0: return new DecimalFormat(*this); michael@0: } michael@0: michael@0: michael@0: FixedDecimal michael@0: DecimalFormat::getFixedDecimal(double number, UErrorCode &status) const { michael@0: FixedDecimal result; michael@0: michael@0: if (U_FAILURE(status)) { michael@0: return result; michael@0: } michael@0: michael@0: if (uprv_isNaN(number) || uprv_isPositiveInfinity(fabs(number))) { michael@0: // For NaN and Infinity the state of the formatter is ignored. michael@0: result.init(number); michael@0: return result; michael@0: } michael@0: michael@0: if (fMultiplier == NULL && fScale == 0 && fRoundingIncrement == 0 && areSignificantDigitsUsed() == FALSE && michael@0: result.quickInit(number) && result.visibleDecimalDigitCount <= getMaximumFractionDigits()) { michael@0: // Fast Path. Construction of an exact FixedDecimal directly from the double, without passing michael@0: // through a DigitList, was successful, and the formatter is doing nothing tricky with rounding. michael@0: // printf("getFixedDecimal(%g): taking fast path.\n", number); michael@0: result.adjustForMinFractionDigits(getMinimumFractionDigits()); michael@0: } else { michael@0: // Slow path. Create a DigitList, and have this formatter round it according to the michael@0: // requirements of the format, and fill the fixedDecimal from that. michael@0: DigitList digits; michael@0: digits.set(number); michael@0: result = getFixedDecimal(digits, status); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // MSVC optimizer bug? michael@0: // turn off optimization as it causes different behavior in the int64->double->int64 conversion michael@0: #if defined (_MSC_VER) michael@0: #pragma optimize ( "", off ) michael@0: #endif michael@0: FixedDecimal michael@0: DecimalFormat::getFixedDecimal(const Formattable &number, UErrorCode &status) const { michael@0: if (U_FAILURE(status)) { michael@0: return FixedDecimal(); michael@0: } michael@0: if (!number.isNumeric()) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return FixedDecimal(); michael@0: } michael@0: michael@0: DigitList *dl = number.getDigitList(); michael@0: if (dl != NULL) { michael@0: DigitList clonedDL(*dl); michael@0: return getFixedDecimal(clonedDL, status); michael@0: } michael@0: michael@0: Formattable::Type type = number.getType(); michael@0: if (type == Formattable::kDouble || type == Formattable::kLong) { michael@0: return getFixedDecimal(number.getDouble(status), status); michael@0: } michael@0: michael@0: if (type == Formattable::kInt64) { michael@0: // "volatile" here is a workaround to avoid optimization issues. michael@0: volatile double fdv = number.getDouble(status); michael@0: // Note: conversion of int64_t -> double rounds with some compilers to michael@0: // values beyond what can be represented as a 64 bit int. Subsequent michael@0: // testing or conversion with int64_t produces bad results. michael@0: // So filter the problematic values, route them to DigitList. michael@0: if (fdv != (double)U_INT64_MAX && fdv != (double)U_INT64_MIN && michael@0: number.getInt64() == (int64_t)fdv) { michael@0: return getFixedDecimal(number.getDouble(status), status); michael@0: } michael@0: } michael@0: michael@0: // The only case left is type==int64_t, with a value with more digits than a double can represent. michael@0: // Any formattable originating as a big decimal will have had a pre-existing digit list. michael@0: // Any originating as a double or int32 will have been handled as a double. michael@0: michael@0: U_ASSERT(type == Formattable::kInt64); michael@0: DigitList digits; michael@0: digits.set(number.getInt64()); michael@0: return getFixedDecimal(digits, status); michael@0: } michael@0: // end workaround MSVC optimizer bug michael@0: #if defined (_MSC_VER) michael@0: #pragma optimize ( "", on ) michael@0: #endif michael@0: michael@0: michael@0: // Create a fixed decimal from a DigitList. michael@0: // The digit list may be modified. michael@0: // Internal function only. michael@0: FixedDecimal michael@0: DecimalFormat::getFixedDecimal(DigitList &number, UErrorCode &status) const { michael@0: // Round the number according to the requirements of this Format. michael@0: FixedDecimal result; michael@0: _round(number, number, result.isNegative, status); michael@0: michael@0: // The int64_t fields in FixedDecimal can easily overflow. michael@0: // In deciding what to discard in this event, consider that fixedDecimal michael@0: // is being used only with PluralRules, and those rules mostly look at least significant michael@0: // few digits of the integer part, and whether the fraction part is zero or not. michael@0: // michael@0: // So, in case of overflow when filling in the fields of the FixedDecimal object, michael@0: // for the integer part, discard the most significant digits. michael@0: // for the fraction part, discard the least significant digits, michael@0: // don't truncate the fraction value to zero. michael@0: // For simplicity, the int64_t fields are limited to 18 decimal digits, even michael@0: // though they could hold most (but not all) 19 digit values. michael@0: michael@0: // Integer Digits. michael@0: int32_t di = number.getDecimalAt()-18; // Take at most 18 digits. michael@0: if (di < 0) { michael@0: di = 0; michael@0: } michael@0: result.intValue = 0; michael@0: for (; di 0) { michael@0: // The number is something like 100000000000000000000000. michael@0: // More than 18 digits integer digits, but the least significant 18 are all zero. michael@0: // We don't want to return zero as the int part, but want to keep zeros michael@0: // for several of the least significant digits. michael@0: result.intValue = 100000000000000000LL; michael@0: } michael@0: michael@0: // Fraction digits. michael@0: result.decimalDigits = result.decimalDigitsWithoutTrailingZeros = result.visibleDecimalDigitCount = 0; michael@0: for (di = number.getDecimalAt(); di < number.getCount(); di++) { michael@0: result.visibleDecimalDigitCount++; michael@0: if (result.decimalDigits < 100000000000000000LL) { michael@0: // 9223372036854775807 Largest 64 bit signed integer michael@0: int32_t digitVal = number.getDigit(di) & 0x0f; // getDigit() returns a char, '0'-'9'. michael@0: result.decimalDigits = result.decimalDigits * 10 + digitVal; michael@0: if (digitVal > 0) { michael@0: result.decimalDigitsWithoutTrailingZeros = result.decimalDigits; michael@0: } michael@0: } michael@0: } michael@0: michael@0: result.hasIntegerValue = (result.decimalDigits == 0); michael@0: michael@0: // Trailing fraction zeros. The format specification may require more trailing michael@0: // zeros than the numeric value. Add any such on now. michael@0: michael@0: int32_t minFractionDigits; michael@0: if (areSignificantDigitsUsed()) { michael@0: minFractionDigits = getMinimumSignificantDigits() - number.getDecimalAt(); michael@0: if (minFractionDigits < 0) { michael@0: minFractionDigits = 0; michael@0: } michael@0: } else { michael@0: minFractionDigits = getMinimumFractionDigits(); michael@0: } michael@0: result.adjustForMinFractionDigits(minFractionDigits); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format(int32_t number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& fieldPosition) const michael@0: { michael@0: return format((int64_t)number, appendTo, fieldPosition); michael@0: } michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format(int32_t number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& fieldPosition, michael@0: UErrorCode& status) const michael@0: { michael@0: return format((int64_t)number, appendTo, fieldPosition, status); michael@0: } michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format(int32_t number, michael@0: UnicodeString& appendTo, michael@0: FieldPositionIterator* posIter, michael@0: UErrorCode& status) const michael@0: { michael@0: return format((int64_t)number, appendTo, posIter, status); michael@0: } michael@0: michael@0: michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: void DecimalFormat::handleChanged() { michael@0: DecimalFormatInternal &data = internalData(fReserved); michael@0: michael@0: if(data.fFastFormatStatus == kFastpathUNKNOWN || data.fFastParseStatus == kFastpathUNKNOWN) { michael@0: return; // still constructing. Wait. michael@0: } michael@0: michael@0: data.fFastParseStatus = data.fFastFormatStatus = kFastpathNO; michael@0: michael@0: #if UCONFIG_HAVE_PARSEALLINPUT michael@0: if(fParseAllInput == UNUM_NO) { michael@0: debug("No Parse fastpath: fParseAllInput==UNUM_NO"); michael@0: } else michael@0: #endif michael@0: if (fFormatWidth!=0) { michael@0: debug("No Parse fastpath: fFormatWidth"); michael@0: } else if(fPositivePrefix.length()>0) { michael@0: debug("No Parse fastpath: positive prefix"); michael@0: } else if(fPositiveSuffix.length()>0) { michael@0: debug("No Parse fastpath: positive suffix"); michael@0: } else if(fNegativePrefix.length()>1 michael@0: || ((fNegativePrefix.length()==1) && (fNegativePrefix.charAt(0)!=0x002D))) { michael@0: debug("No Parse fastpath: negative prefix that isn't '-'"); michael@0: } else if(fNegativeSuffix.length()>0) { michael@0: debug("No Parse fastpath: negative suffix"); michael@0: } else { michael@0: data.fFastParseStatus = kFastpathYES; michael@0: debug("parse fastpath: YES"); michael@0: } michael@0: michael@0: if (fGroupingSize!=0 && isGroupingUsed()) { michael@0: debug("No format fastpath: fGroupingSize!=0 and grouping is used"); michael@0: #ifdef FMT_DEBUG michael@0: printf("groupingsize=%d\n", fGroupingSize); michael@0: #endif michael@0: } else if(fGroupingSize2!=0 && isGroupingUsed()) { michael@0: debug("No format fastpath: fGroupingSize2!=0"); michael@0: } else if(fUseExponentialNotation) { michael@0: debug("No format fastpath: fUseExponentialNotation"); michael@0: } else if(fFormatWidth!=0) { michael@0: debug("No format fastpath: fFormatWidth!=0"); michael@0: } else if(fMinSignificantDigits!=1) { michael@0: debug("No format fastpath: fMinSignificantDigits!=1"); michael@0: } else if(fMultiplier!=NULL) { michael@0: debug("No format fastpath: fMultiplier!=NULL"); michael@0: } else if(fScale!=0) { michael@0: debug("No format fastpath: fScale!=0"); michael@0: } else if(0x0030 != getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0)) { michael@0: debug("No format fastpath: 0x0030 != getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0)"); michael@0: } else if(fDecimalSeparatorAlwaysShown) { michael@0: debug("No format fastpath: fDecimalSeparatorAlwaysShown"); michael@0: } else if(getMinimumFractionDigits()>0) { michael@0: debug("No format fastpath: fMinFractionDigits>0"); michael@0: } else if(fCurrencySignCount != fgCurrencySignCountZero) { michael@0: debug("No format fastpath: fCurrencySignCount != fgCurrencySignCountZero"); michael@0: } else if(fRoundingIncrement!=0) { michael@0: debug("No format fastpath: fRoundingIncrement!=0"); michael@0: } else { michael@0: data.fFastFormatStatus = kFastpathYES; michael@0: debug("format:kFastpathYES!"); michael@0: } michael@0: michael@0: michael@0: } michael@0: #endif michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format(int64_t number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& fieldPosition) const michael@0: { michael@0: UErrorCode status = U_ZERO_ERROR; /* ignored */ michael@0: FieldPositionOnlyHandler handler(fieldPosition); michael@0: return _format(number, appendTo, handler, status); michael@0: } michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format(int64_t number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& fieldPosition, michael@0: UErrorCode& status) const michael@0: { michael@0: FieldPositionOnlyHandler handler(fieldPosition); michael@0: return _format(number, appendTo, handler, status); michael@0: } michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format(int64_t number, michael@0: UnicodeString& appendTo, michael@0: FieldPositionIterator* posIter, michael@0: UErrorCode& status) const michael@0: { michael@0: FieldPositionIteratorHandler handler(posIter, status); michael@0: return _format(number, appendTo, handler, status); michael@0: } michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::_format(int64_t number, michael@0: UnicodeString& appendTo, michael@0: FieldPositionHandler& handler, michael@0: UErrorCode &status) const michael@0: { michael@0: // Bottleneck function for formatting int64_t michael@0: if (U_FAILURE(status)) { michael@0: return appendTo; michael@0: } michael@0: michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: // const UnicodeString *posPrefix = fPosPrefixPattern; michael@0: // const UnicodeString *posSuffix = fPosSuffixPattern; michael@0: // const UnicodeString *negSuffix = fNegSuffixPattern; michael@0: michael@0: const DecimalFormatInternal &data = internalData(fReserved); michael@0: michael@0: #ifdef FMT_DEBUG michael@0: data.dump(); michael@0: printf("fastpath? [%d]\n", number); michael@0: #endif michael@0: michael@0: if( data.fFastFormatStatus==kFastpathYES) { michael@0: michael@0: #define kZero 0x0030 michael@0: const int32_t MAX_IDX = MAX_DIGITS+2; michael@0: UChar outputStr[MAX_IDX]; michael@0: int32_t destIdx = MAX_IDX; michael@0: outputStr[--destIdx] = 0; // term michael@0: michael@0: int64_t n = number; michael@0: if (number < 1) { michael@0: // Negative numbers are slightly larger than positive michael@0: // output the first digit (or the leading zero) michael@0: outputStr[--destIdx] = (-(n % 10) + kZero); michael@0: n /= -10; michael@0: } michael@0: // get any remaining digits michael@0: while (n > 0) { michael@0: outputStr[--destIdx] = (n % 10) + kZero; michael@0: n /= 10; michael@0: } michael@0: michael@0: michael@0: // Slide the number to the start of the output str michael@0: U_ASSERT(destIdx >= 0); michael@0: int32_t length = MAX_IDX - destIdx -1; michael@0: /*int32_t prefixLen = */ appendAffix(appendTo, number, handler, number<0, TRUE); michael@0: int32_t maxIntDig = getMaximumIntegerDigits(); michael@0: int32_t destlength = length<=maxIntDig?length:maxIntDig; // dest length pinned to max int digits michael@0: michael@0: if(length>maxIntDig && fBoolFlags.contains(UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS)) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: michael@0: int32_t prependZero = getMinimumIntegerDigits() - destlength; michael@0: michael@0: #ifdef FMT_DEBUG michael@0: printf("prependZero=%d, length=%d, minintdig=%d maxintdig=%d destlength=%d skip=%d\n", prependZero, length, getMinimumIntegerDigits(), maxIntDig, destlength, length-destlength); michael@0: #endif michael@0: int32_t intBegin = appendTo.length(); michael@0: michael@0: while((prependZero--)>0) { michael@0: appendTo.append((UChar)0x0030); // '0' michael@0: } michael@0: michael@0: appendTo.append(outputStr+destIdx+ michael@0: (length-destlength), // skip any leading digits michael@0: destlength); michael@0: handler.addAttribute(kIntegerField, intBegin, appendTo.length()); michael@0: michael@0: /*int32_t suffixLen =*/ appendAffix(appendTo, number, handler, number<0, FALSE); michael@0: michael@0: //outputStr[length]=0; michael@0: michael@0: #ifdef FMT_DEBUG michael@0: printf("Writing [%s] length [%d] max %d for [%d]\n", outputStr+destIdx, length, MAX_IDX, number); michael@0: #endif michael@0: michael@0: #undef kZero michael@0: michael@0: return appendTo; michael@0: } // end fastpath michael@0: #endif michael@0: michael@0: // Else the slow way - via DigitList michael@0: DigitList digits; michael@0: digits.set(number); michael@0: return _format(digits, appendTo, handler, status); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format( double number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& fieldPosition) const michael@0: { michael@0: UErrorCode status = U_ZERO_ERROR; /* ignored */ michael@0: FieldPositionOnlyHandler handler(fieldPosition); michael@0: return _format(number, appendTo, handler, status); michael@0: } michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format( double number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& fieldPosition, michael@0: UErrorCode& status) const michael@0: { michael@0: FieldPositionOnlyHandler handler(fieldPosition); michael@0: return _format(number, appendTo, handler, status); michael@0: } michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format( double number, michael@0: UnicodeString& appendTo, michael@0: FieldPositionIterator* posIter, michael@0: UErrorCode& status) const michael@0: { michael@0: FieldPositionIteratorHandler handler(posIter, status); michael@0: return _format(number, appendTo, handler, status); michael@0: } michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::_format( double number, michael@0: UnicodeString& appendTo, michael@0: FieldPositionHandler& handler, michael@0: UErrorCode &status) const michael@0: { michael@0: if (U_FAILURE(status)) { michael@0: return appendTo; michael@0: } michael@0: // Special case for NaN, sets the begin and end index to be the michael@0: // the string length of localized name of NaN. michael@0: // TODO: let NaNs go through DigitList. michael@0: if (uprv_isNaN(number)) michael@0: { michael@0: int begin = appendTo.length(); michael@0: appendTo += getConstSymbol(DecimalFormatSymbols::kNaNSymbol); michael@0: michael@0: handler.addAttribute(kIntegerField, begin, appendTo.length()); michael@0: michael@0: addPadding(appendTo, handler, 0, 0); michael@0: return appendTo; michael@0: } michael@0: michael@0: DigitList digits; michael@0: digits.set(number); michael@0: _format(digits, appendTo, handler, status); michael@0: // No way to return status from here. michael@0: return appendTo; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format(const StringPiece &number, michael@0: UnicodeString &toAppendTo, michael@0: FieldPositionIterator *posIter, michael@0: UErrorCode &status) const michael@0: { michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: // don't bother if the int64 path is not optimized michael@0: int32_t len = number.length(); michael@0: michael@0: if(len>0&&len<10) { /* 10 or more digits may not be an int64 */ michael@0: const char *data = number.data(); michael@0: int64_t num = 0; michael@0: UBool neg = FALSE; michael@0: UBool ok = TRUE; michael@0: michael@0: int32_t start = 0; michael@0: michael@0: if(data[start]=='+') { michael@0: start++; michael@0: } else if(data[start]=='-') { michael@0: neg=TRUE; michael@0: start++; michael@0: } michael@0: michael@0: int32_t place = 1; /* 1, 10, ... */ michael@0: for(int32_t i=len-1;i>=start;i--) { michael@0: if(data[i]>='0'&&data[i]<='9') { michael@0: num+=place*(int64_t)(data[i]-'0'); michael@0: } else { michael@0: ok=FALSE; michael@0: break; michael@0: } michael@0: place *= 10; michael@0: } michael@0: michael@0: if(ok) { michael@0: if(neg) { michael@0: num = -num;// add minus bit michael@0: } michael@0: // format as int64_t michael@0: return format(num, toAppendTo, posIter, status); michael@0: } michael@0: // else fall through michael@0: } michael@0: #endif michael@0: michael@0: DigitList dnum; michael@0: dnum.set(number, status); michael@0: if (U_FAILURE(status)) { michael@0: return toAppendTo; michael@0: } michael@0: FieldPositionIteratorHandler handler(posIter, status); michael@0: _format(dnum, toAppendTo, handler, status); michael@0: return toAppendTo; michael@0: } michael@0: michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format(const DigitList &number, michael@0: UnicodeString &appendTo, michael@0: FieldPositionIterator *posIter, michael@0: UErrorCode &status) const { michael@0: FieldPositionIteratorHandler handler(posIter, status); michael@0: _format(number, appendTo, handler, status); michael@0: return appendTo; michael@0: } michael@0: michael@0: michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::format(const DigitList &number, michael@0: UnicodeString& appendTo, michael@0: FieldPosition& pos, michael@0: UErrorCode &status) const { michael@0: FieldPositionOnlyHandler handler(pos); michael@0: _format(number, appendTo, handler, status); michael@0: return appendTo; michael@0: } michael@0: michael@0: DigitList& michael@0: DecimalFormat::_round(const DigitList &number, DigitList &adjustedNum, UBool& isNegative, UErrorCode &status) const { michael@0: if (U_FAILURE(status)) { michael@0: return adjustedNum; michael@0: } michael@0: michael@0: // note: number and adjustedNum may refer to the same DigitList, in cases where a copy michael@0: // is not needed by the caller. michael@0: michael@0: adjustedNum = number; michael@0: isNegative = false; michael@0: if (number.isNaN()) { michael@0: return adjustedNum; michael@0: } michael@0: michael@0: // Do this BEFORE checking to see if value is infinite or negative! Sets the michael@0: // begin and end index to be length of the string composed of michael@0: // localized name of Infinite and the positive/negative localized michael@0: // signs. michael@0: michael@0: adjustedNum.setRoundingMode(fRoundingMode); michael@0: if (fMultiplier != NULL) { michael@0: adjustedNum.mult(*fMultiplier, status); michael@0: if (U_FAILURE(status)) { michael@0: return adjustedNum; michael@0: } michael@0: } michael@0: michael@0: if (fScale != 0) { michael@0: DigitList ten; michael@0: ten.set((int32_t)10); michael@0: if (fScale > 0) { michael@0: for (int32_t i = fScale ; i > 0 ; i--) { michael@0: adjustedNum.mult(ten, status); michael@0: if (U_FAILURE(status)) { michael@0: return adjustedNum; michael@0: } michael@0: } michael@0: } else { michael@0: for (int32_t i = fScale ; i < 0 ; i++) { michael@0: adjustedNum.div(ten, status); michael@0: if (U_FAILURE(status)) { michael@0: return adjustedNum; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Note: sign is important for zero as well as non-zero numbers. michael@0: * Proper detection of -0.0 is needed to deal with the michael@0: * issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98. michael@0: */ michael@0: isNegative = !adjustedNum.isPositive(); michael@0: michael@0: // Apply rounding after multiplier michael@0: michael@0: adjustedNum.fContext.status &= ~DEC_Inexact; michael@0: if (fRoundingIncrement != NULL) { michael@0: adjustedNum.div(*fRoundingIncrement, status); michael@0: adjustedNum.toIntegralValue(); michael@0: adjustedNum.mult(*fRoundingIncrement, status); michael@0: adjustedNum.trim(); michael@0: if (U_FAILURE(status)) { michael@0: return adjustedNum; michael@0: } michael@0: } michael@0: if (fRoundingMode == kRoundUnnecessary && (adjustedNum.fContext.status & DEC_Inexact)) { michael@0: status = U_FORMAT_INEXACT_ERROR; michael@0: return adjustedNum; michael@0: } michael@0: michael@0: if (adjustedNum.isInfinite()) { michael@0: return adjustedNum; michael@0: } michael@0: michael@0: if (fUseExponentialNotation || areSignificantDigitsUsed()) { michael@0: int32_t sigDigits = precision(); michael@0: if (sigDigits > 0) { michael@0: adjustedNum.round(sigDigits); michael@0: } michael@0: } else { michael@0: // Fixed point format. Round to a set number of fraction digits. michael@0: int32_t numFractionDigits = precision(); michael@0: adjustedNum.roundFixedPoint(numFractionDigits); michael@0: } michael@0: if (fRoundingMode == kRoundUnnecessary && (adjustedNum.fContext.status & DEC_Inexact)) { michael@0: status = U_FORMAT_INEXACT_ERROR; michael@0: return adjustedNum; michael@0: } michael@0: return adjustedNum; michael@0: } michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::_format(const DigitList &number, michael@0: UnicodeString& appendTo, michael@0: FieldPositionHandler& handler, michael@0: UErrorCode &status) const michael@0: { michael@0: if (U_FAILURE(status)) { michael@0: return appendTo; michael@0: } michael@0: michael@0: // Special case for NaN, sets the begin and end index to be the michael@0: // the string length of localized name of NaN. michael@0: if (number.isNaN()) michael@0: { michael@0: int begin = appendTo.length(); michael@0: appendTo += getConstSymbol(DecimalFormatSymbols::kNaNSymbol); michael@0: michael@0: handler.addAttribute(kIntegerField, begin, appendTo.length()); michael@0: michael@0: addPadding(appendTo, handler, 0, 0); michael@0: return appendTo; michael@0: } michael@0: michael@0: DigitList adjustedNum; michael@0: UBool isNegative; michael@0: _round(number, adjustedNum, isNegative, status); michael@0: if (U_FAILURE(status)) { michael@0: return appendTo; michael@0: } michael@0: michael@0: // Special case for INFINITE, michael@0: if (adjustedNum.isInfinite()) { michael@0: int32_t prefixLen = appendAffix(appendTo, adjustedNum.getDouble(), handler, isNegative, TRUE); michael@0: michael@0: int begin = appendTo.length(); michael@0: appendTo += getConstSymbol(DecimalFormatSymbols::kInfinitySymbol); michael@0: michael@0: handler.addAttribute(kIntegerField, begin, appendTo.length()); michael@0: michael@0: int32_t suffixLen = appendAffix(appendTo, adjustedNum.getDouble(), handler, isNegative, FALSE); michael@0: michael@0: addPadding(appendTo, handler, prefixLen, suffixLen); michael@0: return appendTo; michael@0: } michael@0: return subformat(appendTo, handler, adjustedNum, FALSE, status); michael@0: } michael@0: michael@0: /** michael@0: * Return true if a grouping separator belongs at the given michael@0: * position, based on whether grouping is in use and the values of michael@0: * the primary and secondary grouping interval. michael@0: * @param pos the number of integer digits to the right of michael@0: * the current position. Zero indicates the position after the michael@0: * rightmost integer digit. michael@0: * @return true if a grouping character belongs at the current michael@0: * position. michael@0: */ michael@0: UBool DecimalFormat::isGroupingPosition(int32_t pos) const { michael@0: UBool result = FALSE; michael@0: if (isGroupingUsed() && (pos > 0) && (fGroupingSize > 0)) { michael@0: if ((fGroupingSize2 > 0) && (pos > fGroupingSize)) { michael@0: result = ((pos - fGroupingSize) % fGroupingSize2) == 0; michael@0: } else { michael@0: result = pos % fGroupingSize == 0; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: /** michael@0: * Complete the formatting of a finite number. On entry, the DigitList must michael@0: * be filled in with the correct digits. michael@0: */ michael@0: UnicodeString& michael@0: DecimalFormat::subformat(UnicodeString& appendTo, michael@0: FieldPositionHandler& handler, michael@0: DigitList& digits, michael@0: UBool isInteger, michael@0: UErrorCode& status) const michael@0: { michael@0: // char zero = '0'; michael@0: // DigitList returns digits as '0' thru '9', so we will need to michael@0: // always need to subtract the character 0 to get the numeric value to use for indexing. michael@0: michael@0: UChar32 localizedDigits[10]; michael@0: localizedDigits[0] = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); michael@0: localizedDigits[1] = getConstSymbol(DecimalFormatSymbols::kOneDigitSymbol).char32At(0); michael@0: localizedDigits[2] = getConstSymbol(DecimalFormatSymbols::kTwoDigitSymbol).char32At(0); michael@0: localizedDigits[3] = getConstSymbol(DecimalFormatSymbols::kThreeDigitSymbol).char32At(0); michael@0: localizedDigits[4] = getConstSymbol(DecimalFormatSymbols::kFourDigitSymbol).char32At(0); michael@0: localizedDigits[5] = getConstSymbol(DecimalFormatSymbols::kFiveDigitSymbol).char32At(0); michael@0: localizedDigits[6] = getConstSymbol(DecimalFormatSymbols::kSixDigitSymbol).char32At(0); michael@0: localizedDigits[7] = getConstSymbol(DecimalFormatSymbols::kSevenDigitSymbol).char32At(0); michael@0: localizedDigits[8] = getConstSymbol(DecimalFormatSymbols::kEightDigitSymbol).char32At(0); michael@0: localizedDigits[9] = getConstSymbol(DecimalFormatSymbols::kNineDigitSymbol).char32At(0); michael@0: michael@0: const UnicodeString *grouping ; michael@0: if(fCurrencySignCount == fgCurrencySignCountZero) { michael@0: grouping = &getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); michael@0: }else{ michael@0: grouping = &getConstSymbol(DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); michael@0: } michael@0: const UnicodeString *decimal; michael@0: if(fCurrencySignCount == fgCurrencySignCountZero) { michael@0: decimal = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); michael@0: } else { michael@0: decimal = &getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); michael@0: } michael@0: UBool useSigDig = areSignificantDigitsUsed(); michael@0: int32_t maxIntDig = getMaximumIntegerDigits(); michael@0: int32_t minIntDig = getMinimumIntegerDigits(); michael@0: michael@0: // Appends the prefix. michael@0: double doubleValue = digits.getDouble(); michael@0: int32_t prefixLen = appendAffix(appendTo, doubleValue, handler, !digits.isPositive(), TRUE); michael@0: michael@0: if (fUseExponentialNotation) michael@0: { michael@0: int currentLength = appendTo.length(); michael@0: int intBegin = currentLength; michael@0: int intEnd = -1; michael@0: int fracBegin = -1; michael@0: michael@0: int32_t minFracDig = 0; michael@0: if (useSigDig) { michael@0: maxIntDig = minIntDig = 1; michael@0: minFracDig = getMinimumSignificantDigits() - 1; michael@0: } else { michael@0: minFracDig = getMinimumFractionDigits(); michael@0: if (maxIntDig > kMaxScientificIntegerDigits) { michael@0: maxIntDig = 1; michael@0: if (maxIntDig < minIntDig) { michael@0: maxIntDig = minIntDig; michael@0: } michael@0: } michael@0: if (maxIntDig > minIntDig) { michael@0: minIntDig = 1; michael@0: } michael@0: } michael@0: michael@0: // Minimum integer digits are handled in exponential format by michael@0: // adjusting the exponent. For example, 0.01234 with 3 minimum michael@0: // integer digits is "123.4E-4". michael@0: michael@0: // Maximum integer digits are interpreted as indicating the michael@0: // repeating range. This is useful for engineering notation, in michael@0: // which the exponent is restricted to a multiple of 3. For michael@0: // example, 0.01234 with 3 maximum integer digits is "12.34e-3". michael@0: // If maximum integer digits are defined and are larger than michael@0: // minimum integer digits, then minimum integer digits are michael@0: // ignored. michael@0: digits.reduce(); // Removes trailing zero digits. michael@0: int32_t exponent = digits.getDecimalAt(); michael@0: if (maxIntDig > 1 && maxIntDig != minIntDig) { michael@0: // A exponent increment is defined; adjust to it. michael@0: exponent = (exponent > 0) ? (exponent - 1) / maxIntDig michael@0: : (exponent / maxIntDig) - 1; michael@0: exponent *= maxIntDig; michael@0: } else { michael@0: // No exponent increment is defined; use minimum integer digits. michael@0: // If none is specified, as in "#E0", generate 1 integer digit. michael@0: exponent -= (minIntDig > 0 || minFracDig > 0) michael@0: ? minIntDig : 1; michael@0: } michael@0: michael@0: // We now output a minimum number of digits, and more if there michael@0: // are more digits, up to the maximum number of digits. We michael@0: // place the decimal point after the "integer" digits, which michael@0: // are the first (decimalAt - exponent) digits. michael@0: int32_t minimumDigits = minIntDig + minFracDig; michael@0: // The number of integer digits is handled specially if the number michael@0: // is zero, since then there may be no digits. michael@0: int32_t integerDigits = digits.isZero() ? minIntDig : michael@0: digits.getDecimalAt() - exponent; michael@0: int32_t totalDigits = digits.getCount(); michael@0: if (minimumDigits > totalDigits) michael@0: totalDigits = minimumDigits; michael@0: if (integerDigits > totalDigits) michael@0: totalDigits = integerDigits; michael@0: michael@0: // totalDigits records total number of digits needs to be processed michael@0: int32_t i; michael@0: for (i=0; i 0) { michael@0: handler.addAttribute(kFractionField, fracBegin, currentLength); michael@0: } michael@0: michael@0: // The exponent is output using the pattern-specified minimum michael@0: // exponent digits. There is no maximum limit to the exponent michael@0: // digits, since truncating the exponent would appendTo in an michael@0: // unacceptable inaccuracy. michael@0: appendTo += getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); michael@0: michael@0: handler.addAttribute(kExponentSymbolField, currentLength, appendTo.length()); michael@0: currentLength = appendTo.length(); michael@0: michael@0: // For zero values, we force the exponent to zero. We michael@0: // must do this here, and not earlier, because the value michael@0: // is used to determine integer digit count above. michael@0: if (digits.isZero()) michael@0: exponent = 0; michael@0: michael@0: if (exponent < 0) { michael@0: appendTo += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); michael@0: handler.addAttribute(kExponentSignField, currentLength, appendTo.length()); michael@0: } else if (fExponentSignAlwaysShown) { michael@0: appendTo += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); michael@0: handler.addAttribute(kExponentSignField, currentLength, appendTo.length()); michael@0: } michael@0: michael@0: currentLength = appendTo.length(); michael@0: michael@0: DigitList expDigits; michael@0: expDigits.set(exponent); michael@0: { michael@0: int expDig = fMinExponentDigits; michael@0: if (fUseExponentialNotation && expDig < 1) { michael@0: expDig = 1; michael@0: } michael@0: for (i=expDigits.getDecimalAt(); i 0 && count < digits.getDecimalAt()) { michael@0: count = digits.getDecimalAt(); michael@0: } michael@0: michael@0: // Handle the case where getMaximumIntegerDigits() is smaller michael@0: // than the real number of integer digits. If this is so, we michael@0: // output the least significant max integer digits. For example, michael@0: // the value 1997 printed with 2 max integer digits is just "97". michael@0: michael@0: int32_t digitIndex = 0; // Index into digitList.fDigits[] michael@0: if (count > maxIntDig && maxIntDig >= 0) { michael@0: count = maxIntDig; michael@0: digitIndex = digits.getDecimalAt() - count; michael@0: if(fBoolFlags.contains(UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS)) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: } michael@0: michael@0: int32_t sizeBeforeIntegerPart = appendTo.length(); michael@0: michael@0: int32_t i; michael@0: for (i=count-1; i>=0; --i) michael@0: { michael@0: if (i < digits.getDecimalAt() && digitIndex < digits.getCount() && michael@0: sigCount < maxSigDig) { michael@0: // Output a real digit michael@0: appendTo += (UChar32)localizedDigits[digits.getDigitValue(digitIndex++)]; michael@0: ++sigCount; michael@0: } michael@0: else michael@0: { michael@0: // Output a zero (leading or trailing) michael@0: appendTo += localizedDigits[0]; michael@0: if (sigCount > 0) { michael@0: ++sigCount; michael@0: } michael@0: } michael@0: michael@0: // Output grouping separator if necessary. michael@0: if (isGroupingPosition(i)) { michael@0: currentLength = appendTo.length(); michael@0: appendTo.append(*grouping); michael@0: handler.addAttribute(kGroupingSeparatorField, currentLength, appendTo.length()); michael@0: } michael@0: } michael@0: michael@0: // This handles the special case of formatting 0. For zero only, we count the michael@0: // zero to the left of the decimal point as one signficant digit. Ordinarily we michael@0: // do not count any leading 0's as significant. If the number we are formatting michael@0: // is not zero, then either sigCount or digits.getCount() will be non-zero. michael@0: if (sigCount == 0 && digits.getCount() == 0) { michael@0: sigCount = 1; michael@0: } michael@0: michael@0: // TODO(dlf): this looks like it was a bug, we marked the int field as ending michael@0: // before the zero was generated. michael@0: // Record field information for caller. michael@0: // if (fieldPosition.getField() == NumberFormat::kIntegerField) michael@0: // fieldPosition.setEndIndex(appendTo.length()); michael@0: michael@0: // Determine whether or not there are any printable fractional michael@0: // digits. If we've used up the digits we know there aren't. michael@0: UBool fractionPresent = (!isInteger && digitIndex < digits.getCount()) || michael@0: (useSigDig ? (sigCount < minSigDig) : (getMinimumFractionDigits() > 0)); michael@0: michael@0: // If there is no fraction present, and we haven't printed any michael@0: // integer digits, then print a zero. Otherwise we won't print michael@0: // _any_ digits, and we won't be able to parse this string. michael@0: if (!fractionPresent && appendTo.length() == sizeBeforeIntegerPart) michael@0: appendTo += localizedDigits[0]; michael@0: michael@0: currentLength = appendTo.length(); michael@0: handler.addAttribute(kIntegerField, intBegin, currentLength); michael@0: michael@0: // Output the decimal separator if we always do so. michael@0: if (fDecimalSeparatorAlwaysShown || fractionPresent) { michael@0: appendTo += *decimal; michael@0: handler.addAttribute(kDecimalSeparatorField, currentLength, appendTo.length()); michael@0: currentLength = appendTo.length(); michael@0: } michael@0: michael@0: int fracBegin = currentLength; michael@0: michael@0: count = useSigDig ? INT32_MAX : getMaximumFractionDigits(); michael@0: if (useSigDig && (sigCount == maxSigDig || michael@0: (sigCount >= minSigDig && digitIndex == digits.getCount()))) { michael@0: count = 0; michael@0: } michael@0: michael@0: for (i=0; i < count; ++i) { michael@0: // Here is where we escape from the loop. We escape michael@0: // if we've output the maximum fraction digits michael@0: // (specified in the for expression above). We also michael@0: // stop when we've output the minimum digits and michael@0: // either: we have an integer, so there is no michael@0: // fractional stuff to display, or we're out of michael@0: // significant digits. michael@0: if (!useSigDig && i >= getMinimumFractionDigits() && michael@0: (isInteger || digitIndex >= digits.getCount())) { michael@0: break; michael@0: } michael@0: michael@0: // Output leading fractional zeros. These are zeros michael@0: // that come after the decimal but before any michael@0: // significant digits. These are only output if michael@0: // abs(number being formatted) < 1.0. michael@0: if (-1-i > (digits.getDecimalAt()-1)) { michael@0: appendTo += localizedDigits[0]; michael@0: continue; michael@0: } michael@0: michael@0: // Output a digit, if we have any precision left, or a michael@0: // zero if we don't. We don't want to output noise digits. michael@0: if (!isInteger && digitIndex < digits.getCount()) { michael@0: appendTo += (UChar32)localizedDigits[digits.getDigitValue(digitIndex++)]; michael@0: } else { michael@0: appendTo += localizedDigits[0]; michael@0: } michael@0: michael@0: // If we reach the maximum number of significant michael@0: // digits, or if we output all the real digits and michael@0: // reach the minimum, then we are done. michael@0: ++sigCount; michael@0: if (useSigDig && michael@0: (sigCount == maxSigDig || michael@0: (digitIndex == digits.getCount() && sigCount >= minSigDig))) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: handler.addAttribute(kFractionField, fracBegin, appendTo.length()); michael@0: } michael@0: michael@0: int32_t suffixLen = appendAffix(appendTo, doubleValue, handler, !digits.isPositive(), FALSE); michael@0: michael@0: addPadding(appendTo, handler, prefixLen, suffixLen); michael@0: return appendTo; michael@0: } michael@0: michael@0: /** michael@0: * Inserts the character fPad as needed to expand result to fFormatWidth. michael@0: * @param result the string to be padded michael@0: */ michael@0: void DecimalFormat::addPadding(UnicodeString& appendTo, michael@0: FieldPositionHandler& handler, michael@0: int32_t prefixLen, michael@0: int32_t suffixLen) const michael@0: { michael@0: if (fFormatWidth > 0) { michael@0: int32_t len = fFormatWidth - appendTo.length(); michael@0: if (len > 0) { michael@0: UnicodeString padding; michael@0: for (int32_t i=0; i currAmt(new CurrencyAmount(parseResult, curbuf, ec)); michael@0: if (U_FAILURE(ec)) { michael@0: pos.setIndex(start); // indicate failure michael@0: } else { michael@0: return currAmt.orphan(); michael@0: } michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: /** michael@0: * Parses the given text as a number, optionally providing a currency amount. michael@0: * @param text the string to parse michael@0: * @param result output parameter for the numeric result. michael@0: * @param parsePosition input-output position; on input, the michael@0: * position within text to match; must have 0 <= pos.getIndex() < michael@0: * text.length(); on output, the position after the last matched michael@0: * character. If the parse fails, the position in unchanged upon michael@0: * output. michael@0: * @param currency if non-NULL, it should point to a 4-UChar buffer. michael@0: * In this case the text is parsed as a currency format, and the michael@0: * ISO 4217 code for the parsed currency is put into the buffer. michael@0: * Otherwise the text is parsed as a non-currency format. michael@0: */ michael@0: void DecimalFormat::parse(const UnicodeString& text, michael@0: Formattable& result, michael@0: ParsePosition& parsePosition, michael@0: UChar* currency) const { michael@0: int32_t startIdx, backup; michael@0: int32_t i = startIdx = backup = parsePosition.getIndex(); michael@0: michael@0: // clear any old contents in the result. In particular, clears any DigitList michael@0: // that it may be holding. michael@0: result.setLong(0); michael@0: if (currency != NULL) { michael@0: for (int32_t ci=0; ci<4; ci++) { michael@0: currency[ci] = 0; michael@0: } michael@0: } michael@0: michael@0: // Handle NaN as a special case: michael@0: michael@0: // Skip padding characters, if around prefix michael@0: if (fFormatWidth > 0 && (fPadPosition == kPadBeforePrefix || michael@0: fPadPosition == kPadAfterPrefix)) { michael@0: i = skipPadding(text, i); michael@0: } michael@0: michael@0: if (isLenient()) { michael@0: // skip any leading whitespace michael@0: i = backup = skipUWhiteSpace(text, i); michael@0: } michael@0: michael@0: // If the text is composed of the representation of NaN, returns NaN.length michael@0: const UnicodeString *nan = &getConstSymbol(DecimalFormatSymbols::kNaNSymbol); michael@0: int32_t nanLen = (text.compare(i, nan->length(), *nan) michael@0: ? 0 : nan->length()); michael@0: if (nanLen) { michael@0: i += nanLen; michael@0: if (fFormatWidth > 0 && (fPadPosition == kPadBeforeSuffix || michael@0: fPadPosition == kPadAfterSuffix)) { michael@0: i = skipPadding(text, i); michael@0: } michael@0: parsePosition.setIndex(i); michael@0: result.setDouble(uprv_getNaN()); michael@0: return; michael@0: } michael@0: michael@0: // NaN parse failed; start over michael@0: i = backup; michael@0: parsePosition.setIndex(i); michael@0: michael@0: // status is used to record whether a number is infinite. michael@0: UBool status[fgStatusLength]; michael@0: michael@0: DigitList *digits = result.getInternalDigitList(); // get one from the stack buffer michael@0: if (digits == NULL) { michael@0: return; // no way to report error from here. michael@0: } michael@0: michael@0: if (fCurrencySignCount != fgCurrencySignCountZero) { michael@0: if (!parseForCurrency(text, parsePosition, *digits, michael@0: status, currency)) { michael@0: return; michael@0: } michael@0: } else { michael@0: if (!subparse(text, michael@0: fNegPrefixPattern, fNegSuffixPattern, michael@0: fPosPrefixPattern, fPosSuffixPattern, michael@0: FALSE, UCURR_SYMBOL_NAME, michael@0: parsePosition, *digits, status, currency)) { michael@0: debug("!subparse(...) - rewind"); michael@0: parsePosition.setIndex(startIdx); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // Handle infinity michael@0: if (status[fgStatusInfinite]) { michael@0: double inf = uprv_getInfinity(); michael@0: result.setDouble(digits->isPositive() ? inf : -inf); michael@0: // TODO: set the dl to infinity, and let it fall into the code below. michael@0: } michael@0: michael@0: else { michael@0: michael@0: if (fMultiplier != NULL) { michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: digits->div(*fMultiplier, ec); michael@0: } michael@0: michael@0: if (fScale != 0) { michael@0: DigitList ten; michael@0: ten.set((int32_t)10); michael@0: if (fScale > 0) { michael@0: for (int32_t i = fScale; i > 0; i--) { michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: digits->div(ten,ec); michael@0: } michael@0: } else { michael@0: for (int32_t i = fScale; i < 0; i++) { michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: digits->mult(ten,ec); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Negative zero special case: michael@0: // if parsing integerOnly, change to +0, which goes into an int32 in a Formattable. michael@0: // if not parsing integerOnly, leave as -0, which a double can represent. michael@0: if (digits->isZero() && !digits->isPositive() && isParseIntegerOnly()) { michael@0: digits->setPositive(TRUE); michael@0: } michael@0: result.adoptDigitList(digits); michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: UBool michael@0: DecimalFormat::parseForCurrency(const UnicodeString& text, michael@0: ParsePosition& parsePosition, michael@0: DigitList& digits, michael@0: UBool* status, michael@0: UChar* currency) const { michael@0: int origPos = parsePosition.getIndex(); michael@0: int maxPosIndex = origPos; michael@0: int maxErrorPos = -1; michael@0: // First, parse against current pattern. michael@0: // Since current pattern could be set by applyPattern(), michael@0: // it could be an arbitrary pattern, and it may not be the one michael@0: // defined in current locale. michael@0: UBool tmpStatus[fgStatusLength]; michael@0: ParsePosition tmpPos(origPos); michael@0: DigitList tmpDigitList; michael@0: UBool found; michael@0: if (fStyle == UNUM_CURRENCY_PLURAL) { michael@0: found = subparse(text, michael@0: fNegPrefixPattern, fNegSuffixPattern, michael@0: fPosPrefixPattern, fPosSuffixPattern, michael@0: TRUE, UCURR_LONG_NAME, michael@0: tmpPos, tmpDigitList, tmpStatus, currency); michael@0: } else { michael@0: found = subparse(text, michael@0: fNegPrefixPattern, fNegSuffixPattern, michael@0: fPosPrefixPattern, fPosSuffixPattern, michael@0: TRUE, UCURR_SYMBOL_NAME, michael@0: tmpPos, tmpDigitList, tmpStatus, currency); michael@0: } michael@0: if (found) { michael@0: if (tmpPos.getIndex() > maxPosIndex) { michael@0: maxPosIndex = tmpPos.getIndex(); michael@0: for (int32_t i = 0; i < fgStatusLength; ++i) { michael@0: status[i] = tmpStatus[i]; michael@0: } michael@0: digits = tmpDigitList; michael@0: } michael@0: } else { michael@0: maxErrorPos = tmpPos.getErrorIndex(); michael@0: } michael@0: // Then, parse against affix patterns. michael@0: // Those are currency patterns and currency plural patterns. michael@0: int32_t pos = -1; michael@0: const UHashElement* element = NULL; michael@0: while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) { michael@0: const UHashTok valueTok = element->value; michael@0: const AffixPatternsForCurrency* affixPtn = (AffixPatternsForCurrency*)valueTok.pointer; michael@0: UBool tmpStatus[fgStatusLength]; michael@0: ParsePosition tmpPos(origPos); michael@0: DigitList tmpDigitList; michael@0: michael@0: #ifdef FMT_DEBUG michael@0: debug("trying affix for currency.."); michael@0: affixPtn->dump(); michael@0: #endif michael@0: michael@0: UBool result = subparse(text, michael@0: &affixPtn->negPrefixPatternForCurrency, michael@0: &affixPtn->negSuffixPatternForCurrency, michael@0: &affixPtn->posPrefixPatternForCurrency, michael@0: &affixPtn->posSuffixPatternForCurrency, michael@0: TRUE, affixPtn->patternType, michael@0: tmpPos, tmpDigitList, tmpStatus, currency); michael@0: if (result) { michael@0: found = true; michael@0: if (tmpPos.getIndex() > maxPosIndex) { michael@0: maxPosIndex = tmpPos.getIndex(); michael@0: for (int32_t i = 0; i < fgStatusLength; ++i) { michael@0: status[i] = tmpStatus[i]; michael@0: } michael@0: digits = tmpDigitList; michael@0: } michael@0: } else { michael@0: maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ? michael@0: tmpPos.getErrorIndex() : maxErrorPos; michael@0: } michael@0: } michael@0: // Finally, parse against simple affix to find the match. michael@0: // For example, in TestMonster suite, michael@0: // if the to-be-parsed text is "-\u00A40,00". michael@0: // complexAffixCompare will not find match, michael@0: // since there is no ISO code matches "\u00A4", michael@0: // and the parse stops at "\u00A4". michael@0: // We will just use simple affix comparison (look for exact match) michael@0: // to pass it. michael@0: // michael@0: // TODO: We should parse against simple affix first when michael@0: // output currency is not requested. After the complex currency michael@0: // parsing implementation was introduced, the default currency michael@0: // instance parsing slowed down because of the new code flow. michael@0: // I filed #10312 - Yoshito michael@0: UBool tmpStatus_2[fgStatusLength]; michael@0: ParsePosition tmpPos_2(origPos); michael@0: DigitList tmpDigitList_2; michael@0: michael@0: // Disable complex currency parsing and try it again. michael@0: UBool result = subparse(text, michael@0: &fNegativePrefix, &fNegativeSuffix, michael@0: &fPositivePrefix, &fPositiveSuffix, michael@0: FALSE /* disable complex currency parsing */, UCURR_SYMBOL_NAME, michael@0: tmpPos_2, tmpDigitList_2, tmpStatus_2, michael@0: currency); michael@0: if (result) { michael@0: if (tmpPos_2.getIndex() > maxPosIndex) { michael@0: maxPosIndex = tmpPos_2.getIndex(); michael@0: for (int32_t i = 0; i < fgStatusLength; ++i) { michael@0: status[i] = tmpStatus_2[i]; michael@0: } michael@0: digits = tmpDigitList_2; michael@0: } michael@0: found = true; michael@0: } else { michael@0: maxErrorPos = (tmpPos_2.getErrorIndex() > maxErrorPos) ? michael@0: tmpPos_2.getErrorIndex() : maxErrorPos; michael@0: } michael@0: michael@0: if (!found) { michael@0: //parsePosition.setIndex(origPos); michael@0: parsePosition.setErrorIndex(maxErrorPos); michael@0: } else { michael@0: parsePosition.setIndex(maxPosIndex); michael@0: parsePosition.setErrorIndex(-1); michael@0: } michael@0: return found; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Parse the given text into a number. The text is parsed beginning at michael@0: * parsePosition, until an unparseable character is seen. michael@0: * @param text the string to parse. michael@0: * @param negPrefix negative prefix. michael@0: * @param negSuffix negative suffix. michael@0: * @param posPrefix positive prefix. michael@0: * @param posSuffix positive suffix. michael@0: * @param complexCurrencyParsing whether it is complex currency parsing or not. michael@0: * @param type the currency type to parse against, LONG_NAME only or not. michael@0: * @param parsePosition The position at which to being parsing. Upon michael@0: * return, the first unparsed character. michael@0: * @param digits the DigitList to set to the parsed value. michael@0: * @param status output param containing boolean status flags indicating michael@0: * whether the value was infinite and whether it was positive. michael@0: * @param currency return value for parsed currency, for generic michael@0: * currency parsing mode, or NULL for normal parsing. In generic michael@0: * currency parsing mode, any currency is parsed, not just the michael@0: * currency that this formatter is set to. michael@0: */ michael@0: UBool DecimalFormat::subparse(const UnicodeString& text, michael@0: const UnicodeString* negPrefix, michael@0: const UnicodeString* negSuffix, michael@0: const UnicodeString* posPrefix, michael@0: const UnicodeString* posSuffix, michael@0: UBool complexCurrencyParsing, michael@0: int8_t type, michael@0: ParsePosition& parsePosition, michael@0: DigitList& digits, UBool* status, michael@0: UChar* currency) const michael@0: { michael@0: // The parsing process builds up the number as char string, in the neutral format that michael@0: // will be acceptable to the decNumber library, then at the end passes that string michael@0: // off for conversion to a decNumber. michael@0: UErrorCode err = U_ZERO_ERROR; michael@0: CharString parsedNum; michael@0: digits.setToZero(); michael@0: michael@0: int32_t position = parsePosition.getIndex(); michael@0: int32_t oldStart = position; michael@0: int32_t textLength = text.length(); // One less pointer to follow michael@0: UBool strictParse = !isLenient(); michael@0: UChar32 zero = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); michael@0: const UnicodeString *groupingString = &getConstSymbol(fCurrencySignCount == fgCurrencySignCountZero ? michael@0: DecimalFormatSymbols::kGroupingSeparatorSymbol : DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); michael@0: UChar32 groupingChar = groupingString->char32At(0); michael@0: int32_t groupingStringLength = groupingString->length(); michael@0: int32_t groupingCharLength = U16_LENGTH(groupingChar); michael@0: UBool groupingUsed = isGroupingUsed(); michael@0: #ifdef FMT_DEBUG michael@0: UChar dbgbuf[300]; michael@0: UnicodeString s(dbgbuf,0,300);; michael@0: s.append((UnicodeString)"PARSE \"").append(text.tempSubString(position)).append((UnicodeString)"\" " ); michael@0: #define DBGAPPD(x) if(x) { s.append(UnicodeString(#x "=")); if(x->isEmpty()) { s.append(UnicodeString("")); } else { s.append(*x); } s.append(UnicodeString(" ")); } else { s.append(UnicodeString(#x "=NULL ")); } michael@0: DBGAPPD(negPrefix); michael@0: DBGAPPD(negSuffix); michael@0: DBGAPPD(posPrefix); michael@0: DBGAPPD(posSuffix); michael@0: debugout(s); michael@0: printf("currencyParsing=%d, fFormatWidth=%d, isParseIntegerOnly=%c text.length=%d negPrefLen=%d\n", currencyParsing, fFormatWidth, (isParseIntegerOnly())?'Y':'N', text.length(), negPrefix!=NULL?negPrefix->length():-1); michael@0: #endif michael@0: michael@0: UBool fastParseOk = false; /* TRUE iff fast parse is OK */ michael@0: // UBool fastParseHadDecimal = FALSE; /* true if fast parse saw a decimal point. */ michael@0: const DecimalFormatInternal &data = internalData(fReserved); michael@0: if((data.fFastParseStatus==kFastpathYES) && michael@0: fCurrencySignCount == fgCurrencySignCountZero && michael@0: // (negPrefix!=NULL&&negPrefix->isEmpty()) || michael@0: text.length()>0 && michael@0: text.length()<32 && michael@0: (posPrefix==NULL||posPrefix->isEmpty()) && michael@0: (posSuffix==NULL||posSuffix->isEmpty()) && michael@0: // (negPrefix==NULL||negPrefix->isEmpty()) && michael@0: // (negSuffix==NULL||(negSuffix->isEmpty()) ) && michael@0: TRUE) { // optimized path michael@0: int j=position; michael@0: int l=text.length(); michael@0: int digitCount=0; michael@0: UChar32 ch = text.char32At(j); michael@0: const UnicodeString *decimalString = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); michael@0: UChar32 decimalChar = 0; michael@0: UBool intOnly = FALSE; michael@0: UChar32 lookForGroup = (groupingUsed&&intOnly&&strictParse)?groupingChar:0; michael@0: michael@0: int32_t decimalCount = decimalString->countChar32(0,3); michael@0: if(isParseIntegerOnly()) { michael@0: decimalChar = 0; // not allowed michael@0: intOnly = TRUE; // Don't look for decimals. michael@0: } else if(decimalCount==1) { michael@0: decimalChar = decimalString->char32At(0); // Look for this decimal michael@0: } else if(decimalCount==0) { michael@0: decimalChar=0; // NO decimal set michael@0: } else { michael@0: j=l+1;//Set counter to end of line, so that we break. Unknown decimal situation. michael@0: } michael@0: michael@0: #ifdef FMT_DEBUG michael@0: printf("Preparing to do fastpath parse: decimalChar=U+%04X, groupingChar=U+%04X, first ch=U+%04X intOnly=%c strictParse=%c\n", michael@0: decimalChar, groupingChar, ch, michael@0: (intOnly)?'y':'n', michael@0: (strictParse)?'y':'n'); michael@0: #endif michael@0: if(ch==0x002D) { // '-' michael@0: j=l+1;//=break - negative number. michael@0: michael@0: /* michael@0: parsedNum.append('-',err); michael@0: j+=U16_LENGTH(ch); michael@0: if(j=0 && digit <= 9) { michael@0: parsedNum.append((char)(digit + '0'), err); michael@0: if((digitCount>0) || digit!=0 || j==(l-1)) { michael@0: digitCount++; michael@0: } michael@0: } else if(ch == 0) { // break out michael@0: digitCount=-1; michael@0: break; michael@0: } else if(ch == decimalChar) { michael@0: parsedNum.append((char)('.'), err); michael@0: decimalChar=0; // no more decimals. michael@0: // fastParseHadDecimal=TRUE; michael@0: } else if(ch == lookForGroup) { michael@0: // ignore grouping char. No decimals, so it has to be an ignorable grouping sep michael@0: } else if(intOnly && (lookForGroup!=0) && !u_isdigit(ch)) { michael@0: // parsing integer only and can fall through michael@0: } else { michael@0: digitCount=-1; // fail - fall through to slow parse michael@0: break; michael@0: } michael@0: j+=U16_LENGTH(ch); michael@0: ch = text.char32At(j); // for next michael@0: } michael@0: if( michael@0: ((j==l)||intOnly) // end OR only parsing integer michael@0: && (digitCount>0)) { // and have at least one digit michael@0: #ifdef FMT_DEBUG michael@0: printf("PP -> %d, good = [%s] digitcount=%d, fGroupingSize=%d fGroupingSize2=%d!\n", j, parsedNum.data(), digitCount, fGroupingSize, fGroupingSize2); michael@0: #endif michael@0: fastParseOk=true; // Fast parse OK! michael@0: michael@0: #ifdef SKIP_OPT michael@0: debug("SKIP_OPT"); michael@0: /* for testing, try it the slow way. also */ michael@0: fastParseOk=false; michael@0: parsedNum.clear(); michael@0: #else michael@0: parsePosition.setIndex(position=j); michael@0: status[fgStatusInfinite]=false; michael@0: #endif michael@0: } else { michael@0: // was not OK. reset, retry michael@0: #ifdef FMT_DEBUG michael@0: printf("Fall through: j=%d, l=%d, digitCount=%d\n", j, l, digitCount); michael@0: #endif michael@0: parsedNum.clear(); michael@0: } michael@0: } else { michael@0: #ifdef FMT_DEBUG michael@0: printf("Could not fastpath parse. "); michael@0: printf("fFormatWidth=%d ", fFormatWidth); michael@0: printf("text.length()=%d ", text.length()); michael@0: printf("posPrefix=%p posSuffix=%p ", posPrefix, posSuffix); michael@0: michael@0: printf("\n"); michael@0: #endif michael@0: } michael@0: michael@0: if(!fastParseOk michael@0: #if UCONFIG_HAVE_PARSEALLINPUT michael@0: && fParseAllInput!=UNUM_YES michael@0: #endif michael@0: ) michael@0: { michael@0: // Match padding before prefix michael@0: if (fFormatWidth > 0 && fPadPosition == kPadBeforePrefix) { michael@0: position = skipPadding(text, position); michael@0: } michael@0: michael@0: // Match positive and negative prefixes; prefer longest match. michael@0: int32_t posMatch = compareAffix(text, position, FALSE, TRUE, posPrefix, complexCurrencyParsing, type, currency); michael@0: int32_t negMatch = compareAffix(text, position, TRUE, TRUE, negPrefix, complexCurrencyParsing, type, currency); michael@0: if (posMatch >= 0 && negMatch >= 0) { michael@0: if (posMatch > negMatch) { michael@0: negMatch = -1; michael@0: } else if (negMatch > posMatch) { michael@0: posMatch = -1; michael@0: } michael@0: } michael@0: if (posMatch >= 0) { michael@0: position += posMatch; michael@0: parsedNum.append('+', err); michael@0: } else if (negMatch >= 0) { michael@0: position += negMatch; michael@0: parsedNum.append('-', err); michael@0: } else if (strictParse){ michael@0: parsePosition.setErrorIndex(position); michael@0: return FALSE; michael@0: } else { michael@0: // Temporary set positive. This might be changed after checking suffix michael@0: parsedNum.append('+', err); michael@0: } michael@0: michael@0: // Match padding before prefix michael@0: if (fFormatWidth > 0 && fPadPosition == kPadAfterPrefix) { michael@0: position = skipPadding(text, position); michael@0: } michael@0: michael@0: if (! strictParse) { michael@0: position = skipUWhiteSpace(text, position); michael@0: } michael@0: michael@0: // process digits or Inf, find decimal position michael@0: const UnicodeString *inf = &getConstSymbol(DecimalFormatSymbols::kInfinitySymbol); michael@0: int32_t infLen = (text.compare(position, inf->length(), *inf) michael@0: ? 0 : inf->length()); michael@0: position += infLen; // infLen is non-zero when it does equal to infinity michael@0: status[fgStatusInfinite] = infLen != 0; michael@0: michael@0: if (infLen != 0) { michael@0: parsedNum.append("Infinity", err); michael@0: } else { michael@0: // We now have a string of digits, possibly with grouping symbols, michael@0: // and decimal points. We want to process these into a DigitList. michael@0: // We don't want to put a bunch of leading zeros into the DigitList michael@0: // though, so we keep track of the location of the decimal point, michael@0: // put only significant digits into the DigitList, and adjust the michael@0: // exponent as needed. michael@0: michael@0: michael@0: UBool strictFail = FALSE; // did we exit with a strict parse failure? michael@0: int32_t lastGroup = -1; // where did we last see a grouping separator? michael@0: int32_t digitStart = position; michael@0: int32_t gs2 = fGroupingSize2 == 0 ? fGroupingSize : fGroupingSize2; michael@0: michael@0: const UnicodeString *decimalString; michael@0: if (fCurrencySignCount != fgCurrencySignCountZero) { michael@0: decimalString = &getConstSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); michael@0: } else { michael@0: decimalString = &getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); michael@0: } michael@0: UChar32 decimalChar = decimalString->char32At(0); michael@0: int32_t decimalStringLength = decimalString->length(); michael@0: int32_t decimalCharLength = U16_LENGTH(decimalChar); michael@0: michael@0: UBool sawDecimal = FALSE; michael@0: UChar32 sawDecimalChar = 0xFFFF; michael@0: UBool sawGrouping = FALSE; michael@0: UChar32 sawGroupingChar = 0xFFFF; michael@0: UBool sawDigit = FALSE; michael@0: int32_t backup = -1; michael@0: int32_t digit; michael@0: michael@0: // equivalent grouping and decimal support michael@0: const UnicodeSet *decimalSet = NULL; michael@0: const UnicodeSet *groupingSet = NULL; michael@0: michael@0: if (decimalCharLength == decimalStringLength) { michael@0: decimalSet = DecimalFormatStaticSets::getSimilarDecimals(decimalChar, strictParse); michael@0: } michael@0: michael@0: if (groupingCharLength == groupingStringLength) { michael@0: if (strictParse) { michael@0: groupingSet = fStaticSets->fStrictDefaultGroupingSeparators; michael@0: } else { michael@0: groupingSet = fStaticSets->fDefaultGroupingSeparators; michael@0: } michael@0: } michael@0: michael@0: // We need to test groupingChar and decimalChar separately from groupingSet and decimalSet, if the sets are even initialized. michael@0: // If sawDecimal is TRUE, only consider sawDecimalChar and NOT decimalSet michael@0: // If a character matches decimalSet, don't consider it to be a member of the groupingSet. michael@0: michael@0: // We have to track digitCount ourselves, because digits.fCount will michael@0: // pin when the maximum allowable digits is reached. michael@0: int32_t digitCount = 0; michael@0: int32_t integerDigitCount = 0; michael@0: michael@0: for (; position < textLength; ) michael@0: { michael@0: UChar32 ch = text.char32At(position); michael@0: michael@0: /* We recognize all digit ranges, not only the Latin digit range michael@0: * '0'..'9'. We do so by using the Character.digit() method, michael@0: * which converts a valid Unicode digit to the range 0..9. michael@0: * michael@0: * The character 'ch' may be a digit. If so, place its value michael@0: * from 0 to 9 in 'digit'. First try using the locale digit, michael@0: * which may or MAY NOT be a standard Unicode digit range. If michael@0: * this fails, try using the standard Unicode digit ranges by michael@0: * calling Character.digit(). If this also fails, digit will michael@0: * have a value outside the range 0..9. michael@0: */ michael@0: digit = ch - zero; michael@0: if (digit < 0 || digit > 9) michael@0: { michael@0: digit = u_charDigitValue(ch); michael@0: } michael@0: michael@0: // As a last resort, look through the localized digits if the zero digit michael@0: // is not a "standard" Unicode digit. michael@0: if ( (digit < 0 || digit > 9) && u_charDigitValue(zero) != 0) { michael@0: digit = 0; michael@0: if ( getConstSymbol((DecimalFormatSymbols::ENumberFormatSymbol)(DecimalFormatSymbols::kZeroDigitSymbol)).char32At(0) == ch ) { michael@0: break; michael@0: } michael@0: for (digit = 1 ; digit < 10 ; digit++ ) { michael@0: if ( getConstSymbol((DecimalFormatSymbols::ENumberFormatSymbol)(DecimalFormatSymbols::kOneDigitSymbol+digit-1)).char32At(0) == ch ) { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (digit >= 0 && digit <= 9) michael@0: { michael@0: if (strictParse && backup != -1) { michael@0: // comma followed by digit, so group before comma is a michael@0: // secondary group. If there was a group separator michael@0: // before that, the group must == the secondary group michael@0: // length, else it can be <= the the secondary group michael@0: // length. michael@0: if ((lastGroup != -1 && backup - lastGroup - 1 != gs2) || michael@0: (lastGroup == -1 && position - digitStart - 1 > gs2)) { michael@0: strictFail = TRUE; michael@0: break; michael@0: } michael@0: michael@0: lastGroup = backup; michael@0: } michael@0: michael@0: // Cancel out backup setting (see grouping handler below) michael@0: backup = -1; michael@0: sawDigit = TRUE; michael@0: michael@0: // Note: this will append leading zeros michael@0: parsedNum.append((char)(digit + '0'), err); michael@0: michael@0: // count any digit that's not a leading zero michael@0: if (digit > 0 || digitCount > 0 || sawDecimal) { michael@0: digitCount += 1; michael@0: michael@0: // count any integer digit that's not a leading zero michael@0: if (! sawDecimal) { michael@0: integerDigitCount += 1; michael@0: } michael@0: } michael@0: michael@0: position += U16_LENGTH(ch); michael@0: } michael@0: else if (groupingStringLength > 0 && michael@0: matchGrouping(groupingChar, sawGrouping, sawGroupingChar, groupingSet, michael@0: decimalChar, decimalSet, michael@0: ch) && groupingUsed) michael@0: { michael@0: if (sawDecimal) { michael@0: break; michael@0: } michael@0: michael@0: if (strictParse) { michael@0: if ((!sawDigit || backup != -1)) { michael@0: // leading group, or two group separators in a row michael@0: strictFail = TRUE; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // Ignore grouping characters, if we are using them, but require michael@0: // that they be followed by a digit. Otherwise we backup and michael@0: // reprocess them. michael@0: backup = position; michael@0: position += groupingStringLength; michael@0: sawGrouping=TRUE; michael@0: // Once we see a grouping character, we only accept that grouping character from then on. michael@0: sawGroupingChar=ch; michael@0: } michael@0: else if (matchDecimal(decimalChar,sawDecimal,sawDecimalChar, decimalSet, ch)) michael@0: { michael@0: if (strictParse) { michael@0: if (backup != -1 || michael@0: (lastGroup != -1 && position - lastGroup != fGroupingSize + 1)) { michael@0: strictFail = TRUE; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // If we're only parsing integers, or if we ALREADY saw the michael@0: // decimal, then don't parse this one. michael@0: if (isParseIntegerOnly() || sawDecimal) { michael@0: break; michael@0: } michael@0: michael@0: parsedNum.append('.', err); michael@0: position += decimalStringLength; michael@0: sawDecimal = TRUE; michael@0: // Once we see a decimal character, we only accept that decimal character from then on. michael@0: sawDecimalChar=ch; michael@0: // decimalSet is considered to consist of (ch,ch) michael@0: } michael@0: else { michael@0: michael@0: if(!fBoolFlags.contains(UNUM_PARSE_NO_EXPONENT) || // don't parse if this is set unless.. michael@0: isScientificNotation()) { // .. it's an exponent format - ignore setting and parse anyways michael@0: const UnicodeString *tmp; michael@0: tmp = &getConstSymbol(DecimalFormatSymbols::kExponentialSymbol); michael@0: // TODO: CASE michael@0: if (!text.caseCompare(position, tmp->length(), *tmp, U_FOLD_CASE_DEFAULT)) // error code is set below if !sawDigit michael@0: { michael@0: // Parse sign, if present michael@0: int32_t pos = position + tmp->length(); michael@0: char exponentSign = '+'; michael@0: michael@0: if (pos < textLength) michael@0: { michael@0: tmp = &getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); michael@0: if (!text.compare(pos, tmp->length(), *tmp)) michael@0: { michael@0: pos += tmp->length(); michael@0: } michael@0: else { michael@0: tmp = &getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); michael@0: if (!text.compare(pos, tmp->length(), *tmp)) michael@0: { michael@0: exponentSign = '-'; michael@0: pos += tmp->length(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: UBool sawExponentDigit = FALSE; michael@0: while (pos < textLength) { michael@0: ch = text[(int32_t)pos]; michael@0: digit = ch - zero; michael@0: michael@0: if (digit < 0 || digit > 9) { michael@0: digit = u_charDigitValue(ch); michael@0: } michael@0: if (0 <= digit && digit <= 9) { michael@0: if (!sawExponentDigit) { michael@0: parsedNum.append('E', err); michael@0: parsedNum.append(exponentSign, err); michael@0: sawExponentDigit = TRUE; michael@0: } michael@0: ++pos; michael@0: parsedNum.append((char)(digit + '0'), err); michael@0: } else { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (sawExponentDigit) { michael@0: position = pos; // Advance past the exponent michael@0: } michael@0: michael@0: break; // Whether we fail or succeed, we exit this loop michael@0: } else { michael@0: break; michael@0: } michael@0: } else { // not parsing exponent michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (backup != -1) michael@0: { michael@0: position = backup; michael@0: } michael@0: michael@0: if (strictParse && !sawDecimal) { michael@0: if (lastGroup != -1 && position - lastGroup != fGroupingSize + 1) { michael@0: strictFail = TRUE; michael@0: } michael@0: } michael@0: michael@0: if (strictFail) { michael@0: // only set with strictParse and a grouping separator error michael@0: michael@0: parsePosition.setIndex(oldStart); michael@0: parsePosition.setErrorIndex(position); michael@0: debug("strictFail!"); michael@0: return FALSE; michael@0: } michael@0: michael@0: // If there was no decimal point we have an integer michael@0: michael@0: // If none of the text string was recognized. For example, parse michael@0: // "x" with pattern "#0.00" (return index and error index both 0) michael@0: // parse "$" with pattern "$#0.00". (return index 0 and error index michael@0: // 1). michael@0: if (!sawDigit && digitCount == 0) { michael@0: #ifdef FMT_DEBUG michael@0: debug("none of text rec"); michael@0: printf("position=%d\n",position); michael@0: #endif michael@0: parsePosition.setIndex(oldStart); michael@0: parsePosition.setErrorIndex(oldStart); michael@0: return FALSE; michael@0: } michael@0: } michael@0: michael@0: // Match padding before suffix michael@0: if (fFormatWidth > 0 && fPadPosition == kPadBeforeSuffix) { michael@0: position = skipPadding(text, position); michael@0: } michael@0: michael@0: int32_t posSuffixMatch = -1, negSuffixMatch = -1; michael@0: michael@0: // Match positive and negative suffixes; prefer longest match. michael@0: if (posMatch >= 0 || (!strictParse && negMatch < 0)) { michael@0: posSuffixMatch = compareAffix(text, position, FALSE, FALSE, posSuffix, complexCurrencyParsing, type, currency); michael@0: } michael@0: if (negMatch >= 0) { michael@0: negSuffixMatch = compareAffix(text, position, TRUE, FALSE, negSuffix, complexCurrencyParsing, type, currency); michael@0: } michael@0: if (posSuffixMatch >= 0 && negSuffixMatch >= 0) { michael@0: if (posSuffixMatch > negSuffixMatch) { michael@0: negSuffixMatch = -1; michael@0: } else if (negSuffixMatch > posSuffixMatch) { michael@0: posSuffixMatch = -1; michael@0: } michael@0: } michael@0: michael@0: // Fail if neither or both michael@0: if (strictParse && ((posSuffixMatch >= 0) == (negSuffixMatch >= 0))) { michael@0: parsePosition.setErrorIndex(position); michael@0: debug("neither or both"); michael@0: return FALSE; michael@0: } michael@0: michael@0: position += (posSuffixMatch >= 0 ? posSuffixMatch : (negSuffixMatch >= 0 ? negSuffixMatch : 0)); michael@0: michael@0: // Match padding before suffix michael@0: if (fFormatWidth > 0 && fPadPosition == kPadAfterSuffix) { michael@0: position = skipPadding(text, position); michael@0: } michael@0: michael@0: parsePosition.setIndex(position); michael@0: michael@0: parsedNum.data()[0] = (posSuffixMatch >= 0 || (!strictParse && negMatch < 0 && negSuffixMatch < 0)) ? '+' : '-'; michael@0: #ifdef FMT_DEBUG michael@0: printf("PP -> %d, SLOW = [%s]! pp=%d, os=%d, err=%s\n", position, parsedNum.data(), parsePosition.getIndex(),oldStart,u_errorName(err)); michael@0: #endif michael@0: } /* end SLOW parse */ michael@0: if(parsePosition.getIndex() == oldStart) michael@0: { michael@0: #ifdef FMT_DEBUG michael@0: printf(" PP didnt move, err\n"); michael@0: #endif michael@0: parsePosition.setErrorIndex(position); michael@0: return FALSE; michael@0: } michael@0: #if UCONFIG_HAVE_PARSEALLINPUT michael@0: else if (fParseAllInput==UNUM_YES&&parsePosition.getIndex()!=textLength) michael@0: { michael@0: #ifdef FMT_DEBUG michael@0: printf(" PP didnt consume all (UNUM_YES), err\n"); michael@0: #endif michael@0: parsePosition.setErrorIndex(position); michael@0: return FALSE; michael@0: } michael@0: #endif michael@0: // uint32_t bits = (fastParseOk?kFastpathOk:0) | michael@0: // (fastParseHadDecimal?0:kNoDecimal); michael@0: //printf("FPOK=%d, FPHD=%d, bits=%08X\n", fastParseOk, fastParseHadDecimal, bits); michael@0: digits.set(parsedNum.toStringPiece(), michael@0: err, michael@0: 0//bits michael@0: ); michael@0: michael@0: if (U_FAILURE(err)) { michael@0: #ifdef FMT_DEBUG michael@0: printf(" err setting %s\n", u_errorName(err)); michael@0: #endif michael@0: parsePosition.setErrorIndex(position); michael@0: return FALSE; michael@0: } michael@0: return TRUE; michael@0: } michael@0: michael@0: /** michael@0: * Starting at position, advance past a run of pad characters, if any. michael@0: * Return the index of the first character after position that is not a pad michael@0: * character. Result is >= position. michael@0: */ michael@0: int32_t DecimalFormat::skipPadding(const UnicodeString& text, int32_t position) const { michael@0: int32_t padLen = U16_LENGTH(fPad); michael@0: while (position < text.length() && michael@0: text.char32At(position) == fPad) { michael@0: position += padLen; michael@0: } michael@0: return position; michael@0: } michael@0: michael@0: /** michael@0: * Return the length matched by the given affix, or -1 if none. michael@0: * Runs of white space in the affix, match runs of white space in michael@0: * the input. Pattern white space and input white space are michael@0: * determined differently; see code. michael@0: * @param text input text michael@0: * @param pos offset into input at which to begin matching michael@0: * @param isNegative michael@0: * @param isPrefix michael@0: * @param affixPat affix pattern used for currency affix comparison. michael@0: * @param complexCurrencyParsing whether it is currency parsing or not michael@0: * @param type the currency type to parse against, LONG_NAME only or not. michael@0: * @param currency return value for parsed currency, for generic michael@0: * currency parsing mode, or null for normal parsing. In generic michael@0: * currency parsing mode, any currency is parsed, not just the michael@0: * currency that this formatter is set to. michael@0: * @return length of input that matches, or -1 if match failure michael@0: */ michael@0: int32_t DecimalFormat::compareAffix(const UnicodeString& text, michael@0: int32_t pos, michael@0: UBool isNegative, michael@0: UBool isPrefix, michael@0: const UnicodeString* affixPat, michael@0: UBool complexCurrencyParsing, michael@0: int8_t type, michael@0: UChar* currency) const michael@0: { michael@0: const UnicodeString *patternToCompare; michael@0: if (fCurrencyChoice != NULL || currency != NULL || michael@0: (fCurrencySignCount != fgCurrencySignCountZero && complexCurrencyParsing)) { michael@0: michael@0: if (affixPat != NULL) { michael@0: return compareComplexAffix(*affixPat, text, pos, type, currency); michael@0: } michael@0: } michael@0: michael@0: if (isNegative) { michael@0: if (isPrefix) { michael@0: patternToCompare = &fNegativePrefix; michael@0: } michael@0: else { michael@0: patternToCompare = &fNegativeSuffix; michael@0: } michael@0: } michael@0: else { michael@0: if (isPrefix) { michael@0: patternToCompare = &fPositivePrefix; michael@0: } michael@0: else { michael@0: patternToCompare = &fPositiveSuffix; michael@0: } michael@0: } michael@0: return compareSimpleAffix(*patternToCompare, text, pos, isLenient()); michael@0: } michael@0: michael@0: UBool DecimalFormat::equalWithSignCompatibility(UChar32 lhs, UChar32 rhs) const { michael@0: if (lhs == rhs) { michael@0: return TRUE; michael@0: } michael@0: U_ASSERT(fStaticSets != NULL); // should already be loaded michael@0: const UnicodeSet *minusSigns = fStaticSets->fMinusSigns; michael@0: const UnicodeSet *plusSigns = fStaticSets->fPlusSigns; michael@0: return (minusSigns->contains(lhs) && minusSigns->contains(rhs)) || michael@0: (plusSigns->contains(lhs) && plusSigns->contains(rhs)); michael@0: } michael@0: michael@0: // check for LRM 0x200E, RLM 0x200F, ALM 0x061C michael@0: #define IS_BIDI_MARK(c) (c==0x200E || c==0x200F || c==0x061C) michael@0: michael@0: #define TRIM_BUFLEN 32 michael@0: UnicodeString& DecimalFormat::trimMarksFromAffix(const UnicodeString& affix, UnicodeString& trimmedAffix) { michael@0: UChar trimBuf[TRIM_BUFLEN]; michael@0: int32_t affixLen = affix.length(); michael@0: int32_t affixPos, trimLen = 0; michael@0: michael@0: for (affixPos = 0; affixPos < affixLen; affixPos++) { michael@0: UChar c = affix.charAt(affixPos); michael@0: if (!IS_BIDI_MARK(c)) { michael@0: if (trimLen < TRIM_BUFLEN) { michael@0: trimBuf[trimLen++] = c; michael@0: } else { michael@0: trimLen = 0; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: return (trimLen > 0)? trimmedAffix.setTo(trimBuf, trimLen): trimmedAffix.setTo(affix); michael@0: } michael@0: michael@0: /** michael@0: * Return the length matched by the given affix, or -1 if none. michael@0: * Runs of white space in the affix, match runs of white space in michael@0: * the input. Pattern white space and input white space are michael@0: * determined differently; see code. michael@0: * @param affix pattern string, taken as a literal michael@0: * @param input input text michael@0: * @param pos offset into input at which to begin matching michael@0: * @return length of input that matches, or -1 if match failure michael@0: */ michael@0: int32_t DecimalFormat::compareSimpleAffix(const UnicodeString& affix, michael@0: const UnicodeString& input, michael@0: int32_t pos, michael@0: UBool lenient) const { michael@0: int32_t start = pos; michael@0: UnicodeString trimmedAffix; michael@0: // For more efficiency we should keep lazily-created trimmed affixes around in michael@0: // instance variables instead of trimming each time they are used (the next step) michael@0: trimMarksFromAffix(affix, trimmedAffix); michael@0: UChar32 affixChar = trimmedAffix.char32At(0); michael@0: int32_t affixLength = trimmedAffix.length(); michael@0: int32_t inputLength = input.length(); michael@0: int32_t affixCharLength = U16_LENGTH(affixChar); michael@0: UnicodeSet *affixSet; michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: michael@0: U_ASSERT(fStaticSets != NULL); // should already be loaded michael@0: michael@0: if (U_FAILURE(status)) { michael@0: return -1; michael@0: } michael@0: if (!lenient) { michael@0: affixSet = fStaticSets->fStrictDashEquivalents; michael@0: michael@0: // If the trimmedAffix is exactly one character long and that character michael@0: // is in the dash set and the very next input character is also michael@0: // in the dash set, return a match. michael@0: if (affixCharLength == affixLength && affixSet->contains(affixChar)) { michael@0: UChar32 ic = input.char32At(pos); michael@0: if (affixSet->contains(ic)) { michael@0: pos += U16_LENGTH(ic); michael@0: pos = skipBidiMarks(input, pos); // skip any trailing bidi marks michael@0: return pos - start; michael@0: } michael@0: } michael@0: michael@0: for (int32_t i = 0; i < affixLength; ) { michael@0: UChar32 c = trimmedAffix.char32At(i); michael@0: int32_t len = U16_LENGTH(c); michael@0: if (PatternProps::isWhiteSpace(c)) { michael@0: // We may have a pattern like: \u200F \u0020 michael@0: // and input text like: \u200F \u0020 michael@0: // Note that U+200F and U+0020 are Pattern_White_Space but only michael@0: // U+0020 is UWhiteSpace. So we have to first do a direct michael@0: // match of the run of Pattern_White_Space in the pattern, michael@0: // then match any extra characters. michael@0: UBool literalMatch = FALSE; michael@0: while (pos < inputLength) { michael@0: UChar32 ic = input.char32At(pos); michael@0: if (ic == c) { michael@0: literalMatch = TRUE; michael@0: i += len; michael@0: pos += len; michael@0: if (i == affixLength) { michael@0: break; michael@0: } michael@0: c = trimmedAffix.char32At(i); michael@0: len = U16_LENGTH(c); michael@0: if (!PatternProps::isWhiteSpace(c)) { michael@0: break; michael@0: } michael@0: } else if (IS_BIDI_MARK(ic)) { michael@0: pos ++; // just skip over this input text michael@0: } else { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // Advance over run in pattern michael@0: i = skipPatternWhiteSpace(trimmedAffix, i); michael@0: michael@0: // Advance over run in input text michael@0: // Must see at least one white space char in input, michael@0: // unless we've already matched some characters literally. michael@0: int32_t s = pos; michael@0: pos = skipUWhiteSpace(input, pos); michael@0: if (pos == s && !literalMatch) { michael@0: return -1; michael@0: } michael@0: michael@0: // If we skip UWhiteSpace in the input text, we need to skip it in the pattern. michael@0: // Otherwise, the previous lines may have skipped over text (such as U+00A0) that michael@0: // is also in the trimmedAffix. michael@0: i = skipUWhiteSpace(trimmedAffix, i); michael@0: } else { michael@0: UBool match = FALSE; michael@0: while (pos < inputLength) { michael@0: UChar32 ic = input.char32At(pos); michael@0: if (!match && ic == c) { michael@0: i += len; michael@0: pos += len; michael@0: match = TRUE; michael@0: } else if (IS_BIDI_MARK(ic)) { michael@0: pos++; // just skip over this input text michael@0: } else { michael@0: break; michael@0: } michael@0: } michael@0: if (!match) { michael@0: return -1; michael@0: } michael@0: } michael@0: } michael@0: } else { michael@0: UBool match = FALSE; michael@0: michael@0: affixSet = fStaticSets->fDashEquivalents; michael@0: michael@0: if (affixCharLength == affixLength && affixSet->contains(affixChar)) { michael@0: pos = skipUWhiteSpaceAndMarks(input, pos); michael@0: UChar32 ic = input.char32At(pos); michael@0: michael@0: if (affixSet->contains(ic)) { michael@0: pos += U16_LENGTH(ic); michael@0: pos = skipBidiMarks(input, pos); michael@0: return pos - start; michael@0: } michael@0: } michael@0: michael@0: for (int32_t i = 0; i < affixLength; ) michael@0: { michael@0: //i = skipRuleWhiteSpace(trimmedAffix, i); michael@0: i = skipUWhiteSpace(trimmedAffix, i); michael@0: pos = skipUWhiteSpaceAndMarks(input, pos); michael@0: michael@0: if (i >= affixLength || pos >= inputLength) { michael@0: break; michael@0: } michael@0: michael@0: UChar32 c = trimmedAffix.char32At(i); michael@0: UChar32 ic = input.char32At(pos); michael@0: michael@0: if (!equalWithSignCompatibility(ic, c)) { michael@0: return -1; michael@0: } michael@0: michael@0: match = TRUE; michael@0: i += U16_LENGTH(c); michael@0: pos += U16_LENGTH(ic); michael@0: pos = skipBidiMarks(input, pos); michael@0: } michael@0: michael@0: if (affixLength > 0 && ! match) { michael@0: return -1; michael@0: } michael@0: } michael@0: return pos - start; michael@0: } michael@0: michael@0: /** michael@0: * Skip over a run of zero or more Pattern_White_Space characters at michael@0: * pos in text. michael@0: */ michael@0: int32_t DecimalFormat::skipPatternWhiteSpace(const UnicodeString& text, int32_t pos) { michael@0: const UChar* s = text.getBuffer(); michael@0: return (int32_t)(PatternProps::skipWhiteSpace(s + pos, text.length() - pos) - s); michael@0: } michael@0: michael@0: /** michael@0: * Skip over a run of zero or more isUWhiteSpace() characters at pos michael@0: * in text. michael@0: */ michael@0: int32_t DecimalFormat::skipUWhiteSpace(const UnicodeString& text, int32_t pos) { michael@0: while (pos < text.length()) { michael@0: UChar32 c = text.char32At(pos); michael@0: if (!u_isUWhiteSpace(c)) { michael@0: break; michael@0: } michael@0: pos += U16_LENGTH(c); michael@0: } michael@0: return pos; michael@0: } michael@0: michael@0: /** michael@0: * Skip over a run of zero or more isUWhiteSpace() characters or bidi marks at pos michael@0: * in text. michael@0: */ michael@0: int32_t DecimalFormat::skipUWhiteSpaceAndMarks(const UnicodeString& text, int32_t pos) { michael@0: while (pos < text.length()) { michael@0: UChar32 c = text.char32At(pos); michael@0: if (!u_isUWhiteSpace(c) && !IS_BIDI_MARK(c)) { // u_isUWhiteSpace doesn't include LRM,RLM,ALM michael@0: break; michael@0: } michael@0: pos += U16_LENGTH(c); michael@0: } michael@0: return pos; michael@0: } michael@0: michael@0: /** michael@0: * Skip over a run of zero or more bidi marks at pos in text. michael@0: */ michael@0: int32_t DecimalFormat::skipBidiMarks(const UnicodeString& text, int32_t pos) { michael@0: while (pos < text.length()) { michael@0: UChar c = text.charAt(pos); michael@0: if (!IS_BIDI_MARK(c)) { michael@0: break; michael@0: } michael@0: pos++; michael@0: } michael@0: return pos; michael@0: } michael@0: michael@0: /** michael@0: * Return the length matched by the given affix, or -1 if none. michael@0: * @param affixPat pattern string michael@0: * @param input input text michael@0: * @param pos offset into input at which to begin matching michael@0: * @param type the currency type to parse against, LONG_NAME only or not. michael@0: * @param currency return value for parsed currency, for generic michael@0: * currency parsing mode, or null for normal parsing. In generic michael@0: * currency parsing mode, any currency is parsed, not just the michael@0: * currency that this formatter is set to. michael@0: * @return length of input that matches, or -1 if match failure michael@0: */ michael@0: int32_t DecimalFormat::compareComplexAffix(const UnicodeString& affixPat, michael@0: const UnicodeString& text, michael@0: int32_t pos, michael@0: int8_t type, michael@0: UChar* currency) const michael@0: { michael@0: int32_t start = pos; michael@0: U_ASSERT(currency != NULL || michael@0: (fCurrencyChoice != NULL && *getCurrency() != 0) || michael@0: fCurrencySignCount != fgCurrencySignCountZero); michael@0: michael@0: for (int32_t i=0; michael@0: i= 0; ) { michael@0: UChar32 c = affixPat.char32At(i); michael@0: i += U16_LENGTH(c); michael@0: michael@0: if (c == kQuote) { michael@0: U_ASSERT(i <= affixPat.length()); michael@0: c = affixPat.char32At(i); michael@0: i += U16_LENGTH(c); michael@0: michael@0: const UnicodeString* affix = NULL; michael@0: michael@0: switch (c) { michael@0: case kCurrencySign: { michael@0: // since the currency names in choice format is saved michael@0: // the same way as other currency names, michael@0: // do not need to do currency choice parsing here. michael@0: // the general currency parsing parse against all names, michael@0: // including names in choice format. michael@0: UBool intl = igetLocale().getName(); michael@0: ParsePosition ppos(pos); michael@0: UChar curr[4]; michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: // Delegate parse of display name => ISO code to Currency michael@0: uprv_parseCurrency(loc, text, ppos, type, curr, ec); michael@0: michael@0: // If parse succeeds, populate currency[0] michael@0: if (U_SUCCESS(ec) && ppos.getIndex() != pos) { michael@0: if (currency) { michael@0: u_strcpy(currency, curr); michael@0: } else { michael@0: // The formatter is currency-style but the client has not requested michael@0: // the value of the parsed currency. In this case, if that value does michael@0: // not match the formatter's current value, then the parse fails. michael@0: UChar effectiveCurr[4]; michael@0: getEffectiveCurrency(effectiveCurr, ec); michael@0: if ( U_FAILURE(ec) || u_strncmp(curr,effectiveCurr,4) != 0 ) { michael@0: pos = -1; michael@0: continue; michael@0: } michael@0: } michael@0: pos = ppos.getIndex(); michael@0: } else if (!isLenient()){ michael@0: pos = -1; michael@0: } michael@0: continue; michael@0: } michael@0: case kPatternPercent: michael@0: affix = &getConstSymbol(DecimalFormatSymbols::kPercentSymbol); michael@0: break; michael@0: case kPatternPerMill: michael@0: affix = &getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); michael@0: break; michael@0: case kPatternPlus: michael@0: affix = &getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); michael@0: break; michael@0: case kPatternMinus: michael@0: affix = &getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); michael@0: break; michael@0: default: michael@0: // fall through to affix!=0 test, which will fail michael@0: break; michael@0: } michael@0: michael@0: if (affix != NULL) { michael@0: pos = match(text, pos, *affix); michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: pos = match(text, pos, c); michael@0: if (PatternProps::isWhiteSpace(c)) { michael@0: i = skipPatternWhiteSpace(affixPat, i); michael@0: } michael@0: } michael@0: return pos - start; michael@0: } michael@0: michael@0: /** michael@0: * Match a single character at text[pos] and return the index of the michael@0: * next character upon success. Return -1 on failure. If michael@0: * ch is a Pattern_White_Space then match a run of white space in text. michael@0: */ michael@0: int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, UChar32 ch) { michael@0: if (PatternProps::isWhiteSpace(ch)) { michael@0: // Advance over run of white space in input text michael@0: // Must see at least one white space char in input michael@0: int32_t s = pos; michael@0: pos = skipPatternWhiteSpace(text, pos); michael@0: if (pos == s) { michael@0: return -1; michael@0: } michael@0: return pos; michael@0: } michael@0: return (pos >= 0 && text.char32At(pos) == ch) ? michael@0: (pos + U16_LENGTH(ch)) : -1; michael@0: } michael@0: michael@0: /** michael@0: * Match a string at text[pos] and return the index of the next michael@0: * character upon success. Return -1 on failure. Match a run of michael@0: * white space in str with a run of white space in text. michael@0: */ michael@0: int32_t DecimalFormat::match(const UnicodeString& text, int32_t pos, const UnicodeString& str) { michael@0: for (int32_t i=0; i= 0; ) { michael@0: UChar32 ch = str.char32At(i); michael@0: i += U16_LENGTH(ch); michael@0: if (PatternProps::isWhiteSpace(ch)) { michael@0: i = skipPatternWhiteSpace(str, i); michael@0: } michael@0: pos = match(text, pos, ch); michael@0: } michael@0: return pos; michael@0: } michael@0: michael@0: UBool DecimalFormat::matchSymbol(const UnicodeString &text, int32_t position, int32_t length, const UnicodeString &symbol, michael@0: UnicodeSet *sset, UChar32 schar) michael@0: { michael@0: if (sset != NULL) { michael@0: return sset->contains(schar); michael@0: } michael@0: michael@0: return text.compare(position, length, symbol) == 0; michael@0: } michael@0: michael@0: UBool DecimalFormat::matchDecimal(UChar32 symbolChar, michael@0: UBool sawDecimal, UChar32 sawDecimalChar, michael@0: const UnicodeSet *sset, UChar32 schar) { michael@0: if(sawDecimal) { michael@0: return schar==sawDecimalChar; michael@0: } else if(schar==symbolChar) { michael@0: return TRUE; michael@0: } else if(sset!=NULL) { michael@0: return sset->contains(schar); michael@0: } else { michael@0: return FALSE; michael@0: } michael@0: } michael@0: michael@0: UBool DecimalFormat::matchGrouping(UChar32 groupingChar, michael@0: UBool sawGrouping, UChar32 sawGroupingChar, michael@0: const UnicodeSet *sset, michael@0: UChar32 /*decimalChar*/, const UnicodeSet *decimalSet, michael@0: UChar32 schar) { michael@0: if(sawGrouping) { michael@0: return schar==sawGroupingChar; // previously found michael@0: } else if(schar==groupingChar) { michael@0: return TRUE; // char from symbols michael@0: } else if(sset!=NULL) { michael@0: return sset->contains(schar) && // in groupingSet but... michael@0: ((decimalSet==NULL) || !decimalSet->contains(schar)); // Exclude decimalSet from groupingSet michael@0: } else { michael@0: return FALSE; michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Gets the pointer to the localized decimal format symbols michael@0: michael@0: const DecimalFormatSymbols* michael@0: DecimalFormat::getDecimalFormatSymbols() const michael@0: { michael@0: return fSymbols; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // De-owning the current localized symbols and adopt the new symbols. michael@0: michael@0: void michael@0: DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) michael@0: { michael@0: if (symbolsToAdopt == NULL) { michael@0: return; // do not allow caller to set fSymbols to NULL michael@0: } michael@0: michael@0: UBool sameSymbols = FALSE; michael@0: if (fSymbols != NULL) { michael@0: sameSymbols = (UBool)(getConstSymbol(DecimalFormatSymbols::kCurrencySymbol) == michael@0: symbolsToAdopt->getConstSymbol(DecimalFormatSymbols::kCurrencySymbol) && michael@0: getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol) == michael@0: symbolsToAdopt->getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol)); michael@0: delete fSymbols; michael@0: } michael@0: michael@0: fSymbols = symbolsToAdopt; michael@0: if (!sameSymbols) { michael@0: // If the currency symbols are the same, there is no need to recalculate. michael@0: setCurrencyForSymbols(); michael@0: } michael@0: expandAffixes(NULL); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: //------------------------------------------------------------------------------ michael@0: // Setting the symbols is equlivalent to adopting a newly created localized michael@0: // symbols. michael@0: michael@0: void michael@0: DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) michael@0: { michael@0: adoptDecimalFormatSymbols(new DecimalFormatSymbols(symbols)); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: michael@0: const CurrencyPluralInfo* michael@0: DecimalFormat::getCurrencyPluralInfo(void) const michael@0: { michael@0: return fCurrencyPluralInfo; michael@0: } michael@0: michael@0: michael@0: void michael@0: DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) michael@0: { michael@0: if (toAdopt != NULL) { michael@0: delete fCurrencyPluralInfo; michael@0: fCurrencyPluralInfo = toAdopt; michael@0: // re-set currency affix patterns and currency affixes. michael@0: if (fCurrencySignCount != fgCurrencySignCountZero) { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: if (fAffixPatternsForCurrency) { michael@0: deleteHashForAffixPattern(); michael@0: } michael@0: setupCurrencyAffixPatterns(status); michael@0: if (fCurrencySignCount == fgCurrencySignCountInPluralFormat) { michael@0: // only setup the affixes of the plural pattern. michael@0: setupCurrencyAffixes(fFormatPattern, FALSE, TRUE, status); michael@0: } michael@0: } michael@0: } michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) michael@0: { michael@0: adoptCurrencyPluralInfo(info.clone()); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Update the currency object to match the symbols. This method michael@0: * is used only when the caller has passed in a symbols object michael@0: * that may not be the default object for its locale. michael@0: */ michael@0: void michael@0: DecimalFormat::setCurrencyForSymbols() { michael@0: /*Bug 4212072 michael@0: Update the affix strings accroding to symbols in order to keep michael@0: the affix strings up to date. michael@0: [Richard/GCL] michael@0: */ michael@0: michael@0: // With the introduction of the Currency object, the currency michael@0: // symbols in the DFS object are ignored. For backward michael@0: // compatibility, we check any explicitly set DFS object. If it michael@0: // is a default symbols object for its locale, we change the michael@0: // currency object to one for that locale. If it is custom, michael@0: // we set the currency to null. michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: const UChar* c = NULL; michael@0: const char* loc = fSymbols->getLocale().getName(); michael@0: UChar intlCurrencySymbol[4]; michael@0: ucurr_forLocale(loc, intlCurrencySymbol, 4, &ec); michael@0: UnicodeString currencySymbol; michael@0: michael@0: uprv_getStaticCurrencyName(intlCurrencySymbol, loc, currencySymbol, ec); michael@0: if (U_SUCCESS(ec) michael@0: && getConstSymbol(DecimalFormatSymbols::kCurrencySymbol) == currencySymbol michael@0: && getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol) == UnicodeString(intlCurrencySymbol)) michael@0: { michael@0: // Trap an error in mapping locale to currency. If we can't michael@0: // map, then don't fail and set the currency to "". michael@0: c = intlCurrencySymbol; michael@0: } michael@0: ec = U_ZERO_ERROR; // reset local error code! michael@0: setCurrencyInternally(c, ec); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Gets the positive prefix of the number pattern. michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::getPositivePrefix(UnicodeString& result) const michael@0: { michael@0: result = fPositivePrefix; michael@0: return result; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Sets the positive prefix of the number pattern. michael@0: michael@0: void michael@0: DecimalFormat::setPositivePrefix(const UnicodeString& newValue) michael@0: { michael@0: fPositivePrefix = newValue; michael@0: delete fPosPrefixPattern; michael@0: fPosPrefixPattern = 0; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Gets the negative prefix of the number pattern. michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::getNegativePrefix(UnicodeString& result) const michael@0: { michael@0: result = fNegativePrefix; michael@0: return result; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Gets the negative prefix of the number pattern. michael@0: michael@0: void michael@0: DecimalFormat::setNegativePrefix(const UnicodeString& newValue) michael@0: { michael@0: fNegativePrefix = newValue; michael@0: delete fNegPrefixPattern; michael@0: fNegPrefixPattern = 0; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Gets the positive suffix of the number pattern. michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::getPositiveSuffix(UnicodeString& result) const michael@0: { michael@0: result = fPositiveSuffix; michael@0: return result; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Sets the positive suffix of the number pattern. michael@0: michael@0: void michael@0: DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) michael@0: { michael@0: fPositiveSuffix = newValue; michael@0: delete fPosSuffixPattern; michael@0: fPosSuffixPattern = 0; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Gets the negative suffix of the number pattern. michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::getNegativeSuffix(UnicodeString& result) const michael@0: { michael@0: result = fNegativeSuffix; michael@0: return result; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Sets the negative suffix of the number pattern. michael@0: michael@0: void michael@0: DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) michael@0: { michael@0: fNegativeSuffix = newValue; michael@0: delete fNegSuffixPattern; michael@0: fNegSuffixPattern = 0; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Gets the multiplier of the number pattern. michael@0: // Multipliers are stored as decimal numbers (DigitLists) because that michael@0: // is the most convenient for muliplying or dividing the numbers to be formatted. michael@0: // A NULL multiplier implies one, and the scaling operations are skipped. michael@0: michael@0: int32_t michael@0: DecimalFormat::getMultiplier() const michael@0: { michael@0: if (fMultiplier == NULL) { michael@0: return 1; michael@0: } else { michael@0: return fMultiplier->getLong(); michael@0: } michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Sets the multiplier of the number pattern. michael@0: void michael@0: DecimalFormat::setMultiplier(int32_t newValue) michael@0: { michael@0: // if (newValue == 0) { michael@0: // throw new IllegalArgumentException("Bad multiplier: " + newValue); michael@0: // } michael@0: if (newValue == 0) { michael@0: newValue = 1; // one being the benign default value for a multiplier. michael@0: } michael@0: if (newValue == 1) { michael@0: delete fMultiplier; michael@0: fMultiplier = NULL; michael@0: } else { michael@0: if (fMultiplier == NULL) { michael@0: fMultiplier = new DigitList; michael@0: } michael@0: if (fMultiplier != NULL) { michael@0: fMultiplier->set(newValue); michael@0: } michael@0: } michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Get the rounding increment. michael@0: * @return A positive rounding increment, or 0.0 if rounding michael@0: * is not in effect. michael@0: * @see #setRoundingIncrement michael@0: * @see #getRoundingMode michael@0: * @see #setRoundingMode michael@0: */ michael@0: double DecimalFormat::getRoundingIncrement() const { michael@0: if (fRoundingIncrement == NULL) { michael@0: return 0.0; michael@0: } else { michael@0: return fRoundingIncrement->getDouble(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Set the rounding increment. This method also controls whether michael@0: * rounding is enabled. michael@0: * @param newValue A positive rounding increment, or 0.0 to disable rounding. michael@0: * Negative increments are equivalent to 0.0. michael@0: * @see #getRoundingIncrement michael@0: * @see #getRoundingMode michael@0: * @see #setRoundingMode michael@0: */ michael@0: void DecimalFormat::setRoundingIncrement(double newValue) { michael@0: if (newValue > 0.0) { michael@0: if (fRoundingIncrement == NULL) { michael@0: fRoundingIncrement = new DigitList(); michael@0: } michael@0: if (fRoundingIncrement != NULL) { michael@0: fRoundingIncrement->set(newValue); michael@0: return; michael@0: } michael@0: } michael@0: // These statements are executed if newValue is less than 0.0 michael@0: // or fRoundingIncrement could not be created. michael@0: delete fRoundingIncrement; michael@0: fRoundingIncrement = NULL; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Get the rounding mode. michael@0: * @return A rounding mode michael@0: * @see #setRoundingIncrement michael@0: * @see #getRoundingIncrement michael@0: * @see #setRoundingMode michael@0: */ michael@0: DecimalFormat::ERoundingMode DecimalFormat::getRoundingMode() const { michael@0: return fRoundingMode; michael@0: } michael@0: michael@0: /** michael@0: * Set the rounding mode. This has no effect unless the rounding michael@0: * increment is greater than zero. michael@0: * @param roundingMode A rounding mode michael@0: * @see #setRoundingIncrement michael@0: * @see #getRoundingIncrement michael@0: * @see #getRoundingMode michael@0: */ michael@0: void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) { michael@0: fRoundingMode = roundingMode; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Get the width to which the output of format() is padded. michael@0: * @return the format width, or zero if no padding is in effect michael@0: * @see #setFormatWidth michael@0: * @see #getPadCharacter michael@0: * @see #setPadCharacter michael@0: * @see #getPadPosition michael@0: * @see #setPadPosition michael@0: */ michael@0: int32_t DecimalFormat::getFormatWidth() const { michael@0: return fFormatWidth; michael@0: } michael@0: michael@0: /** michael@0: * Set the width to which the output of format() is padded. michael@0: * This method also controls whether padding is enabled. michael@0: * @param width the width to which to pad the result of michael@0: * format(), or zero to disable padding. A negative michael@0: * width is equivalent to 0. michael@0: * @see #getFormatWidth michael@0: * @see #getPadCharacter michael@0: * @see #setPadCharacter michael@0: * @see #getPadPosition michael@0: * @see #setPadPosition michael@0: */ michael@0: void DecimalFormat::setFormatWidth(int32_t width) { michael@0: fFormatWidth = (width > 0) ? width : 0; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: UnicodeString DecimalFormat::getPadCharacterString() const { michael@0: return UnicodeString(fPad); michael@0: } michael@0: michael@0: void DecimalFormat::setPadCharacter(const UnicodeString &padChar) { michael@0: if (padChar.length() > 0) { michael@0: fPad = padChar.char32At(0); michael@0: } michael@0: else { michael@0: fPad = kDefaultPad; michael@0: } michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Get the position at which padding will take place. This is the location michael@0: * at which padding will be inserted if the result of format() michael@0: * is shorter than the format width. michael@0: * @return the pad position, one of kPadBeforePrefix, michael@0: * kPadAfterPrefix, kPadBeforeSuffix, or michael@0: * kPadAfterSuffix. michael@0: * @see #setFormatWidth michael@0: * @see #getFormatWidth michael@0: * @see #setPadCharacter michael@0: * @see #getPadCharacter michael@0: * @see #setPadPosition michael@0: * @see #kPadBeforePrefix michael@0: * @see #kPadAfterPrefix michael@0: * @see #kPadBeforeSuffix michael@0: * @see #kPadAfterSuffix michael@0: */ michael@0: DecimalFormat::EPadPosition DecimalFormat::getPadPosition() const { michael@0: return fPadPosition; michael@0: } michael@0: michael@0: /** michael@0: * NEW michael@0: * Set the position at which padding will take place. This is the location michael@0: * at which padding will be inserted if the result of format() michael@0: * is shorter than the format width. This has no effect unless padding is michael@0: * enabled. michael@0: * @param padPos the pad position, one of kPadBeforePrefix, michael@0: * kPadAfterPrefix, kPadBeforeSuffix, or michael@0: * kPadAfterSuffix. michael@0: * @see #setFormatWidth michael@0: * @see #getFormatWidth michael@0: * @see #setPadCharacter michael@0: * @see #getPadCharacter michael@0: * @see #getPadPosition michael@0: * @see #kPadBeforePrefix michael@0: * @see #kPadAfterPrefix michael@0: * @see #kPadBeforeSuffix michael@0: * @see #kPadAfterSuffix michael@0: */ michael@0: void DecimalFormat::setPadPosition(EPadPosition padPos) { michael@0: fPadPosition = padPos; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Return whether or not scientific notation is used. michael@0: * @return TRUE if this object formats and parses scientific notation michael@0: * @see #setScientificNotation michael@0: * @see #getMinimumExponentDigits michael@0: * @see #setMinimumExponentDigits michael@0: * @see #isExponentSignAlwaysShown michael@0: * @see #setExponentSignAlwaysShown michael@0: */ michael@0: UBool DecimalFormat::isScientificNotation() const { michael@0: return fUseExponentialNotation; michael@0: } michael@0: michael@0: /** michael@0: * Set whether or not scientific notation is used. michael@0: * @param useScientific TRUE if this object formats and parses scientific michael@0: * notation michael@0: * @see #isScientificNotation michael@0: * @see #getMinimumExponentDigits michael@0: * @see #setMinimumExponentDigits michael@0: * @see #isExponentSignAlwaysShown michael@0: * @see #setExponentSignAlwaysShown michael@0: */ michael@0: void DecimalFormat::setScientificNotation(UBool useScientific) { michael@0: fUseExponentialNotation = useScientific; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Return the minimum exponent digits that will be shown. michael@0: * @return the minimum exponent digits that will be shown michael@0: * @see #setScientificNotation michael@0: * @see #isScientificNotation michael@0: * @see #setMinimumExponentDigits michael@0: * @see #isExponentSignAlwaysShown michael@0: * @see #setExponentSignAlwaysShown michael@0: */ michael@0: int8_t DecimalFormat::getMinimumExponentDigits() const { michael@0: return fMinExponentDigits; michael@0: } michael@0: michael@0: /** michael@0: * Set the minimum exponent digits that will be shown. This has no michael@0: * effect unless scientific notation is in use. michael@0: * @param minExpDig a value >= 1 indicating the fewest exponent digits michael@0: * that will be shown. Values less than 1 will be treated as 1. michael@0: * @see #setScientificNotation michael@0: * @see #isScientificNotation michael@0: * @see #getMinimumExponentDigits michael@0: * @see #isExponentSignAlwaysShown michael@0: * @see #setExponentSignAlwaysShown michael@0: */ michael@0: void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) { michael@0: fMinExponentDigits = (int8_t)((minExpDig > 0) ? minExpDig : 1); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Return whether the exponent sign is always shown. michael@0: * @return TRUE if the exponent is always prefixed with either the michael@0: * localized minus sign or the localized plus sign, false if only negative michael@0: * exponents are prefixed with the localized minus sign. michael@0: * @see #setScientificNotation michael@0: * @see #isScientificNotation michael@0: * @see #setMinimumExponentDigits michael@0: * @see #getMinimumExponentDigits michael@0: * @see #setExponentSignAlwaysShown michael@0: */ michael@0: UBool DecimalFormat::isExponentSignAlwaysShown() const { michael@0: return fExponentSignAlwaysShown; michael@0: } michael@0: michael@0: /** michael@0: * Set whether the exponent sign is always shown. This has no effect michael@0: * unless scientific notation is in use. michael@0: * @param expSignAlways TRUE if the exponent is always prefixed with either michael@0: * the localized minus sign or the localized plus sign, false if only michael@0: * negative exponents are prefixed with the localized minus sign. michael@0: * @see #setScientificNotation michael@0: * @see #isScientificNotation michael@0: * @see #setMinimumExponentDigits michael@0: * @see #getMinimumExponentDigits michael@0: * @see #isExponentSignAlwaysShown michael@0: */ michael@0: void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) { michael@0: fExponentSignAlwaysShown = expSignAlways; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Gets the grouping size of the number pattern. For example, thousand or 10 michael@0: // thousand groupings. michael@0: michael@0: int32_t michael@0: DecimalFormat::getGroupingSize() const michael@0: { michael@0: return fGroupingSize; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Gets the grouping size of the number pattern. michael@0: michael@0: void michael@0: DecimalFormat::setGroupingSize(int32_t newValue) michael@0: { michael@0: fGroupingSize = newValue; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: int32_t michael@0: DecimalFormat::getSecondaryGroupingSize() const michael@0: { michael@0: return fGroupingSize2; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: void michael@0: DecimalFormat::setSecondaryGroupingSize(int32_t newValue) michael@0: { michael@0: fGroupingSize2 = newValue; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Checks if to show the decimal separator. michael@0: michael@0: UBool michael@0: DecimalFormat::isDecimalSeparatorAlwaysShown() const michael@0: { michael@0: return fDecimalSeparatorAlwaysShown; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Sets to always show the decimal separator. michael@0: michael@0: void michael@0: DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) michael@0: { michael@0: fDecimalSeparatorAlwaysShown = newValue; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Emits the pattern of this DecimalFormat instance. michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::toPattern(UnicodeString& result) const michael@0: { michael@0: return toPattern(result, FALSE); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Emits the localized pattern this DecimalFormat instance. michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::toLocalizedPattern(UnicodeString& result) const michael@0: { michael@0: return toPattern(result, TRUE); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: /** michael@0: * Expand the affix pattern strings into the expanded affix strings. If any michael@0: * affix pattern string is null, do not expand it. This method should be michael@0: * called any time the symbols or the affix patterns change in order to keep michael@0: * the expanded affix strings up to date. michael@0: * This method also will be called before formatting if format currency michael@0: * plural names, since the plural name is not a static one, it is michael@0: * based on the currency plural count, the affix will be known only michael@0: * after the currency plural count is know. michael@0: * In which case, the parameter michael@0: * 'pluralCount' will be a non-null currency plural count. michael@0: * In all other cases, the 'pluralCount' is null, which means it is not needed. michael@0: */ michael@0: void DecimalFormat::expandAffixes(const UnicodeString* pluralCount) { michael@0: FieldPositionHandler none; michael@0: if (fPosPrefixPattern != 0) { michael@0: expandAffix(*fPosPrefixPattern, fPositivePrefix, 0, none, FALSE, pluralCount); michael@0: } michael@0: if (fPosSuffixPattern != 0) { michael@0: expandAffix(*fPosSuffixPattern, fPositiveSuffix, 0, none, FALSE, pluralCount); michael@0: } michael@0: if (fNegPrefixPattern != 0) { michael@0: expandAffix(*fNegPrefixPattern, fNegativePrefix, 0, none, FALSE, pluralCount); michael@0: } michael@0: if (fNegSuffixPattern != 0) { michael@0: expandAffix(*fNegSuffixPattern, fNegativeSuffix, 0, none, FALSE, pluralCount); michael@0: } michael@0: #ifdef FMT_DEBUG michael@0: UnicodeString s; michael@0: s.append(UnicodeString("[")) michael@0: .append(DEREFSTR(fPosPrefixPattern)).append((UnicodeString)"|").append(DEREFSTR(fPosSuffixPattern)) michael@0: .append((UnicodeString)";") .append(DEREFSTR(fNegPrefixPattern)).append((UnicodeString)"|").append(DEREFSTR(fNegSuffixPattern)) michael@0: .append((UnicodeString)"]->[") michael@0: .append(fPositivePrefix).append((UnicodeString)"|").append(fPositiveSuffix) michael@0: .append((UnicodeString)";") .append(fNegativePrefix).append((UnicodeString)"|").append(fNegativeSuffix) michael@0: .append((UnicodeString)"]\n"); michael@0: debugout(s); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Expand an affix pattern into an affix string. All characters in the michael@0: * pattern are literal unless prefixed by kQuote. The following characters michael@0: * after kQuote are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE, michael@0: * PATTERN_MINUS, and kCurrencySign. If kCurrencySign is doubled (kQuote + michael@0: * kCurrencySign + kCurrencySign), it is interpreted as an international michael@0: * currency sign. If CURRENCY_SIGN is tripled, it is interpreted as michael@0: * currency plural long names, such as "US Dollars". michael@0: * Any other character after a kQuote represents itself. michael@0: * kQuote must be followed by another character; kQuote may not occur by michael@0: * itself at the end of the pattern. michael@0: * michael@0: * This method is used in two distinct ways. First, it is used to expand michael@0: * the stored affix patterns into actual affixes. For this usage, doFormat michael@0: * must be false. Second, it is used to expand the stored affix patterns michael@0: * given a specific number (doFormat == true), for those rare cases in michael@0: * which a currency format references a ChoiceFormat (e.g., en_IN display michael@0: * name for INR). The number itself is taken from digitList. michael@0: * michael@0: * When used in the first way, this method has a side effect: It sets michael@0: * currencyChoice to a ChoiceFormat object, if the currency's display name michael@0: * in this locale is a ChoiceFormat pattern (very rare). It only does this michael@0: * if currencyChoice is null to start with. michael@0: * michael@0: * @param pattern the non-null, fPossibly empty pattern michael@0: * @param affix string to receive the expanded equivalent of pattern. michael@0: * Previous contents are deleted. michael@0: * @param doFormat if false, then the pattern will be expanded, and if a michael@0: * currency symbol is encountered that expands to a ChoiceFormat, the michael@0: * currencyChoice member variable will be initialized if it is null. If michael@0: * doFormat is true, then it is assumed that the currencyChoice has been michael@0: * created, and it will be used to format the value in digitList. michael@0: * @param pluralCount the plural count. It is only used for currency michael@0: * plural format. In which case, it is the plural michael@0: * count of the currency amount. For example, michael@0: * in en_US, it is the singular "one", or the plural michael@0: * "other". For all other cases, it is null, and michael@0: * is not being used. michael@0: */ michael@0: void DecimalFormat::expandAffix(const UnicodeString& pattern, michael@0: UnicodeString& affix, michael@0: double number, michael@0: FieldPositionHandler& handler, michael@0: UBool doFormat, michael@0: const UnicodeString* pluralCount) const { michael@0: affix.remove(); michael@0: for (int i=0; igetLocale().getName() : michael@0: Locale::getDefault().getName(), &isChoiceFormat, michael@0: pluralCountChar.data(), &len, &ec); michael@0: affix += UnicodeString(s, len); michael@0: handler.addAttribute(kCurrencyField, beginIdx, affix.length()); michael@0: } else if(intl) { michael@0: affix.append(currencyUChars, -1); michael@0: handler.addAttribute(kCurrencyField, beginIdx, affix.length()); michael@0: } else { michael@0: int32_t len; michael@0: UBool isChoiceFormat; michael@0: // If fSymbols is NULL, use default locale michael@0: const UChar* s = ucurr_getName(currencyUChars, michael@0: fSymbols != NULL ? fSymbols->getLocale().getName() : Locale::getDefault().getName(), michael@0: UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &ec); michael@0: if (isChoiceFormat) { michael@0: // Two modes here: If doFormat is false, we set up michael@0: // currencyChoice. If doFormat is true, we use the michael@0: // previously created currencyChoice to format the michael@0: // value in digitList. michael@0: if (!doFormat) { michael@0: // If the currency is handled by a ChoiceFormat, michael@0: // then we're not going to use the expanded michael@0: // patterns. Instantiate the ChoiceFormat and michael@0: // return. michael@0: if (fCurrencyChoice == NULL) { michael@0: // TODO Replace double-check with proper thread-safe code michael@0: ChoiceFormat* fmt = new ChoiceFormat(UnicodeString(s), ec); michael@0: if (U_SUCCESS(ec)) { michael@0: umtx_lock(NULL); michael@0: if (fCurrencyChoice == NULL) { michael@0: // Cast away const michael@0: ((DecimalFormat*)this)->fCurrencyChoice = fmt; michael@0: fmt = NULL; michael@0: } michael@0: umtx_unlock(NULL); michael@0: delete fmt; michael@0: } michael@0: } michael@0: // We could almost return null or "" here, since the michael@0: // expanded affixes are almost not used at all michael@0: // in this situation. However, one method -- michael@0: // toPattern() -- still does use the expanded michael@0: // affixes, in order to set up a padding michael@0: // pattern. We use the CURRENCY_SIGN as a michael@0: // placeholder. michael@0: affix.append(kCurrencySign); michael@0: } else { michael@0: if (fCurrencyChoice != NULL) { michael@0: FieldPosition pos(0); // ignored michael@0: if (number < 0) { michael@0: number = -number; michael@0: } michael@0: fCurrencyChoice->format(number, affix, pos); michael@0: } else { michael@0: // We only arrive here if the currency choice michael@0: // format in the locale data is INVALID. michael@0: affix.append(currencyUChars, -1); michael@0: handler.addAttribute(kCurrencyField, beginIdx, affix.length()); michael@0: } michael@0: } michael@0: continue; michael@0: } michael@0: affix += UnicodeString(s, len); michael@0: handler.addAttribute(kCurrencyField, beginIdx, affix.length()); michael@0: } michael@0: } else { michael@0: if(intl) { michael@0: affix += getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); michael@0: } else { michael@0: affix += getConstSymbol(DecimalFormatSymbols::kCurrencySymbol); michael@0: } michael@0: handler.addAttribute(kCurrencyField, beginIdx, affix.length()); michael@0: } michael@0: break; michael@0: } michael@0: case kPatternPercent: michael@0: affix += getConstSymbol(DecimalFormatSymbols::kPercentSymbol); michael@0: handler.addAttribute(kPercentField, beginIdx, affix.length()); michael@0: break; michael@0: case kPatternPerMill: michael@0: affix += getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); michael@0: handler.addAttribute(kPermillField, beginIdx, affix.length()); michael@0: break; michael@0: case kPatternPlus: michael@0: affix += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); michael@0: handler.addAttribute(kSignField, beginIdx, affix.length()); michael@0: break; michael@0: case kPatternMinus: michael@0: affix += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); michael@0: handler.addAttribute(kSignField, beginIdx, affix.length()); michael@0: break; michael@0: default: michael@0: affix.append(c); michael@0: break; michael@0: } michael@0: } michael@0: else { michael@0: affix.append(c); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Append an affix to the given StringBuffer. michael@0: * @param buf buffer to append to michael@0: * @param isNegative michael@0: * @param isPrefix michael@0: */ michael@0: int32_t DecimalFormat::appendAffix(UnicodeString& buf, double number, michael@0: FieldPositionHandler& handler, michael@0: UBool isNegative, UBool isPrefix) const { michael@0: // plural format precedes choice format michael@0: if (fCurrencyChoice != 0 && michael@0: fCurrencySignCount != fgCurrencySignCountInPluralFormat) { michael@0: const UnicodeString* affixPat; michael@0: if (isPrefix) { michael@0: affixPat = isNegative ? fNegPrefixPattern : fPosPrefixPattern; michael@0: } else { michael@0: affixPat = isNegative ? fNegSuffixPattern : fPosSuffixPattern; michael@0: } michael@0: if (affixPat) { michael@0: UnicodeString affixBuf; michael@0: expandAffix(*affixPat, affixBuf, number, handler, TRUE, NULL); michael@0: buf.append(affixBuf); michael@0: return affixBuf.length(); michael@0: } michael@0: // else someone called a function that reset the pattern. michael@0: } michael@0: michael@0: const UnicodeString* affix; michael@0: if (fCurrencySignCount == fgCurrencySignCountInPluralFormat) { michael@0: // TODO: get an accurate count of visible fraction digits. michael@0: UnicodeString pluralCount; michael@0: int32_t minFractionDigits = this->getMinimumFractionDigits(); michael@0: if (minFractionDigits > 0) { michael@0: FixedDecimal ni(number, this->getMinimumFractionDigits()); michael@0: pluralCount = fCurrencyPluralInfo->getPluralRules()->select(ni); michael@0: } else { michael@0: pluralCount = fCurrencyPluralInfo->getPluralRules()->select(number); michael@0: } michael@0: AffixesForCurrency* oneSet; michael@0: if (fStyle == UNUM_CURRENCY_PLURAL) { michael@0: oneSet = (AffixesForCurrency*)fPluralAffixesForCurrency->get(pluralCount); michael@0: } else { michael@0: oneSet = (AffixesForCurrency*)fAffixesForCurrency->get(pluralCount); michael@0: } michael@0: if (isPrefix) { michael@0: affix = isNegative ? &oneSet->negPrefixForCurrency : michael@0: &oneSet->posPrefixForCurrency; michael@0: } else { michael@0: affix = isNegative ? &oneSet->negSuffixForCurrency : michael@0: &oneSet->posSuffixForCurrency; michael@0: } michael@0: } else { michael@0: if (isPrefix) { michael@0: affix = isNegative ? &fNegativePrefix : &fPositivePrefix; michael@0: } else { michael@0: affix = isNegative ? &fNegativeSuffix : &fPositiveSuffix; michael@0: } michael@0: } michael@0: michael@0: int32_t begin = (int) buf.length(); michael@0: michael@0: buf.append(*affix); michael@0: michael@0: if (handler.isRecording()) { michael@0: int32_t offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kCurrencySymbol)); michael@0: if (offset > -1) { michael@0: UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kCurrencySymbol); michael@0: handler.addAttribute(kCurrencyField, begin + offset, begin + offset + aff.length()); michael@0: } michael@0: michael@0: offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol)); michael@0: if (offset > -1) { michael@0: UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); michael@0: handler.addAttribute(kCurrencyField, begin + offset, begin + offset + aff.length()); michael@0: } michael@0: michael@0: offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)); michael@0: if (offset > -1) { michael@0: UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); michael@0: handler.addAttribute(kSignField, begin + offset, begin + offset + aff.length()); michael@0: } michael@0: michael@0: offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kPercentSymbol)); michael@0: if (offset > -1) { michael@0: UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kPercentSymbol); michael@0: handler.addAttribute(kPercentField, begin + offset, begin + offset + aff.length()); michael@0: } michael@0: michael@0: offset = (int) (*affix).indexOf(getConstSymbol(DecimalFormatSymbols::kPerMillSymbol)); michael@0: if (offset > -1) { michael@0: UnicodeString aff = getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); michael@0: handler.addAttribute(kPermillField, begin + offset, begin + offset + aff.length()); michael@0: } michael@0: } michael@0: return affix->length(); michael@0: } michael@0: michael@0: /** michael@0: * Appends an affix pattern to the given StringBuffer, quoting special michael@0: * characters as needed. Uses the internal affix pattern, if that exists, michael@0: * or the literal affix, if the internal affix pattern is null. The michael@0: * appended string will generate the same affix pattern (or literal affix) michael@0: * when passed to toPattern(). michael@0: * michael@0: * @param appendTo the affix string is appended to this michael@0: * @param affixPattern a pattern such as fPosPrefixPattern; may be null michael@0: * @param expAffix a corresponding expanded affix, such as fPositivePrefix. michael@0: * Ignored unless affixPattern is null. If affixPattern is null, then michael@0: * expAffix is appended as a literal affix. michael@0: * @param localized true if the appended pattern should contain localized michael@0: * pattern characters; otherwise, non-localized pattern chars are appended michael@0: */ michael@0: void DecimalFormat::appendAffixPattern(UnicodeString& appendTo, michael@0: const UnicodeString* affixPattern, michael@0: const UnicodeString& expAffix, michael@0: UBool localized) const { michael@0: if (affixPattern == 0) { michael@0: appendAffixPattern(appendTo, expAffix, localized); michael@0: } else { michael@0: int i; michael@0: for (int pos=0; poslength(); pos=i) { michael@0: i = affixPattern->indexOf(kQuote, pos); michael@0: if (i < 0) { michael@0: UnicodeString s; michael@0: affixPattern->extractBetween(pos, affixPattern->length(), s); michael@0: appendAffixPattern(appendTo, s, localized); michael@0: break; michael@0: } michael@0: if (i > pos) { michael@0: UnicodeString s; michael@0: affixPattern->extractBetween(pos, i, s); michael@0: appendAffixPattern(appendTo, s, localized); michael@0: } michael@0: UChar32 c = affixPattern->char32At(++i); michael@0: ++i; michael@0: if (c == kQuote) { michael@0: appendTo.append(c).append(c); michael@0: // Fall through and append another kQuote below michael@0: } else if (c == kCurrencySign && michael@0: ilength() && michael@0: affixPattern->char32At(i) == kCurrencySign) { michael@0: ++i; michael@0: appendTo.append(c).append(c); michael@0: } else if (localized) { michael@0: switch (c) { michael@0: case kPatternPercent: michael@0: appendTo += getConstSymbol(DecimalFormatSymbols::kPercentSymbol); michael@0: break; michael@0: case kPatternPerMill: michael@0: appendTo += getConstSymbol(DecimalFormatSymbols::kPerMillSymbol); michael@0: break; michael@0: case kPatternPlus: michael@0: appendTo += getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol); michael@0: break; michael@0: case kPatternMinus: michael@0: appendTo += getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol); michael@0: break; michael@0: default: michael@0: appendTo.append(c); michael@0: } michael@0: } else { michael@0: appendTo.append(c); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Append an affix to the given StringBuffer, using quotes if michael@0: * there are special characters. Single quotes themselves must be michael@0: * escaped in either case. michael@0: */ michael@0: void michael@0: DecimalFormat::appendAffixPattern(UnicodeString& appendTo, michael@0: const UnicodeString& affix, michael@0: UBool localized) const { michael@0: UBool needQuote; michael@0: if(localized) { michael@0: needQuote = affix.indexOf(getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol)) >= 0 michael@0: || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)) >= 0 michael@0: || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)) >= 0 michael@0: || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPercentSymbol)) >= 0 michael@0: || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPerMillSymbol)) >= 0 michael@0: || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kDigitSymbol)) >= 0 michael@0: || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)) >= 0 michael@0: || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol)) >= 0 michael@0: || affix.indexOf(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)) >= 0 michael@0: || affix.indexOf(kCurrencySign) >= 0; michael@0: } michael@0: else { michael@0: needQuote = affix.indexOf(kPatternZeroDigit) >= 0 michael@0: || affix.indexOf(kPatternGroupingSeparator) >= 0 michael@0: || affix.indexOf(kPatternDecimalSeparator) >= 0 michael@0: || affix.indexOf(kPatternPercent) >= 0 michael@0: || affix.indexOf(kPatternPerMill) >= 0 michael@0: || affix.indexOf(kPatternDigit) >= 0 michael@0: || affix.indexOf(kPatternSeparator) >= 0 michael@0: || affix.indexOf(kPatternExponent) >= 0 michael@0: || affix.indexOf(kPatternPlus) >= 0 michael@0: || affix.indexOf(kPatternMinus) >= 0 michael@0: || affix.indexOf(kCurrencySign) >= 0; michael@0: } michael@0: if (needQuote) michael@0: appendTo += (UChar)0x0027 /*'\''*/; michael@0: if (affix.indexOf((UChar)0x0027 /*'\''*/) < 0) michael@0: appendTo += affix; michael@0: else { michael@0: for (int32_t j = 0; j < affix.length(); ) { michael@0: UChar32 c = affix.char32At(j); michael@0: j += U16_LENGTH(c); michael@0: appendTo += c; michael@0: if (c == 0x0027 /*'\''*/) michael@0: appendTo += c; michael@0: } michael@0: } michael@0: if (needQuote) michael@0: appendTo += (UChar)0x0027 /*'\''*/; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: UnicodeString& michael@0: DecimalFormat::toPattern(UnicodeString& result, UBool localized) const michael@0: { michael@0: if (fStyle == UNUM_CURRENCY_PLURAL) { michael@0: // the prefix or suffix pattern might not be defined yet, michael@0: // so they can not be synthesized, michael@0: // instead, get them directly. michael@0: // but it might not be the actual pattern used in formatting. michael@0: // the actual pattern used in formatting depends on the michael@0: // formatted number's plural count. michael@0: result = fFormatPattern; michael@0: return result; michael@0: } michael@0: result.remove(); michael@0: UChar32 zero, sigDigit = kPatternSignificantDigit; michael@0: UnicodeString digit, group; michael@0: int32_t i; michael@0: int32_t roundingDecimalPos = 0; // Pos of decimal in roundingDigits michael@0: UnicodeString roundingDigits; michael@0: int32_t padPos = (fFormatWidth > 0) ? fPadPosition : -1; michael@0: UnicodeString padSpec; michael@0: UBool useSigDig = areSignificantDigitsUsed(); michael@0: michael@0: if (localized) { michael@0: digit.append(getConstSymbol(DecimalFormatSymbols::kDigitSymbol)); michael@0: group.append(getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)); michael@0: zero = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); michael@0: if (useSigDig) { michael@0: sigDigit = getConstSymbol(DecimalFormatSymbols::kSignificantDigitSymbol).char32At(0); michael@0: } michael@0: } michael@0: else { michael@0: digit.append((UChar)kPatternDigit); michael@0: group.append((UChar)kPatternGroupingSeparator); michael@0: zero = (UChar32)kPatternZeroDigit; michael@0: } michael@0: if (fFormatWidth > 0) { michael@0: if (localized) { michael@0: padSpec.append(getConstSymbol(DecimalFormatSymbols::kPadEscapeSymbol)); michael@0: } michael@0: else { michael@0: padSpec.append((UChar)kPatternPadEscape); michael@0: } michael@0: padSpec.append(fPad); michael@0: } michael@0: if (fRoundingIncrement != NULL) { michael@0: for(i=0; igetCount(); ++i) { michael@0: roundingDigits.append(zero+(fRoundingIncrement->getDigitValue(i))); // Convert to Unicode digit michael@0: } michael@0: roundingDecimalPos = fRoundingIncrement->getDecimalAt(); michael@0: } michael@0: for (int32_t part=0; part<2; ++part) { michael@0: if (padPos == kPadBeforePrefix) { michael@0: result.append(padSpec); michael@0: } michael@0: appendAffixPattern(result, michael@0: (part==0 ? fPosPrefixPattern : fNegPrefixPattern), michael@0: (part==0 ? fPositivePrefix : fNegativePrefix), michael@0: localized); michael@0: if (padPos == kPadAfterPrefix && ! padSpec.isEmpty()) { michael@0: result.append(padSpec); michael@0: } michael@0: int32_t sub0Start = result.length(); michael@0: int32_t g = isGroupingUsed() ? _max(0, fGroupingSize) : 0; michael@0: if (g > 0 && fGroupingSize2 > 0 && fGroupingSize2 != fGroupingSize) { michael@0: g += fGroupingSize2; michael@0: } michael@0: int32_t maxDig = 0, minDig = 0, maxSigDig = 0; michael@0: if (useSigDig) { michael@0: minDig = getMinimumSignificantDigits(); michael@0: maxDig = maxSigDig = getMaximumSignificantDigits(); michael@0: } else { michael@0: minDig = getMinimumIntegerDigits(); michael@0: maxDig = getMaximumIntegerDigits(); michael@0: } michael@0: if (fUseExponentialNotation) { michael@0: if (maxDig > kMaxScientificIntegerDigits) { michael@0: maxDig = 1; michael@0: } michael@0: } else if (useSigDig) { michael@0: maxDig = _max(maxDig, g+1); michael@0: } else { michael@0: maxDig = _max(_max(g, getMinimumIntegerDigits()), michael@0: roundingDecimalPos) + 1; michael@0: } michael@0: for (i = maxDig; i > 0; --i) { michael@0: if (!fUseExponentialNotation && i maxSigDig or 1 <= pos <= (maxSigDig - minSigDig) michael@0: // Use @ if (maxSigDig - minSigDig) < pos <= maxSigDig michael@0: if (maxSigDig >= i && i > (maxSigDig - minDig)) { michael@0: result.append(sigDigit); michael@0: } else { michael@0: result.append(digit); michael@0: } michael@0: } else { michael@0: if (! roundingDigits.isEmpty()) { michael@0: int32_t pos = roundingDecimalPos - i; michael@0: if (pos >= 0 && pos < roundingDigits.length()) { michael@0: result.append((UChar) (roundingDigits.char32At(pos) - kPatternZeroDigit + zero)); michael@0: continue; michael@0: } michael@0: } michael@0: if (i<=minDig) { michael@0: result.append(zero); michael@0: } else { michael@0: result.append(digit); michael@0: } michael@0: } michael@0: } michael@0: if (!useSigDig) { michael@0: if (getMaximumFractionDigits() > 0 || fDecimalSeparatorAlwaysShown) { michael@0: if (localized) { michael@0: result += getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); michael@0: } michael@0: else { michael@0: result.append((UChar)kPatternDecimalSeparator); michael@0: } michael@0: } michael@0: int32_t pos = roundingDecimalPos; michael@0: for (i = 0; i < getMaximumFractionDigits(); ++i) { michael@0: if (! roundingDigits.isEmpty() && pos < roundingDigits.length()) { michael@0: if (pos < 0) { michael@0: result.append(zero); michael@0: } michael@0: else { michael@0: result.append((UChar)(roundingDigits.char32At(pos) - kPatternZeroDigit + zero)); michael@0: } michael@0: ++pos; michael@0: continue; michael@0: } michael@0: if (i 0) { michael@0: result.insert(sub0Start, digit); michael@0: ++maxDig; michael@0: --add; michael@0: // Only add a grouping separator if we have at least michael@0: // 2 additional characters to be added, so we don't michael@0: // end up with ",###". michael@0: if (add>1 && isGroupingPosition(maxDig)) { michael@0: result.insert(sub0Start, group); michael@0: --add; michael@0: } michael@0: } michael@0: } michael@0: if (fPadPosition == kPadBeforeSuffix && ! padSpec.isEmpty()) { michael@0: result.append(padSpec); michael@0: } michael@0: if (part == 0) { michael@0: appendAffixPattern(result, fPosSuffixPattern, fPositiveSuffix, localized); michael@0: if (fPadPosition == kPadAfterSuffix && ! padSpec.isEmpty()) { michael@0: result.append(padSpec); michael@0: } michael@0: UBool isDefault = FALSE; michael@0: if ((fNegSuffixPattern == fPosSuffixPattern && // both null michael@0: fNegativeSuffix == fPositiveSuffix) michael@0: || (fNegSuffixPattern != 0 && fPosSuffixPattern != 0 && michael@0: *fNegSuffixPattern == *fPosSuffixPattern)) michael@0: { michael@0: if (fNegPrefixPattern != NULL && fPosPrefixPattern != NULL) michael@0: { michael@0: int32_t length = fPosPrefixPattern->length(); michael@0: isDefault = fNegPrefixPattern->length() == (length+2) && michael@0: (*fNegPrefixPattern)[(int32_t)0] == kQuote && michael@0: (*fNegPrefixPattern)[(int32_t)1] == kPatternMinus && michael@0: fNegPrefixPattern->compare(2, length, *fPosPrefixPattern, 0, length) == 0; michael@0: } michael@0: if (!isDefault && michael@0: fNegPrefixPattern == NULL && fPosPrefixPattern == NULL) michael@0: { michael@0: int32_t length = fPositivePrefix.length(); michael@0: isDefault = fNegativePrefix.length() == (length+1) && michael@0: fNegativePrefix.compare(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)) == 0 && michael@0: fNegativePrefix.compare(1, length, fPositivePrefix, 0, length) == 0; michael@0: } michael@0: } michael@0: if (isDefault) { michael@0: break; // Don't output default negative subpattern michael@0: } else { michael@0: if (localized) { michael@0: result += getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol); michael@0: } michael@0: else { michael@0: result.append((UChar)kPatternSeparator); michael@0: } michael@0: } michael@0: } else { michael@0: appendAffixPattern(result, fNegSuffixPattern, fNegativeSuffix, localized); michael@0: if (fPadPosition == kPadAfterSuffix && ! padSpec.isEmpty()) { michael@0: result.append(padSpec); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: void michael@0: DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) michael@0: { michael@0: UParseError parseError; michael@0: applyPattern(pattern, FALSE, parseError, status); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: void michael@0: DecimalFormat::applyPattern(const UnicodeString& pattern, michael@0: UParseError& parseError, michael@0: UErrorCode& status) michael@0: { michael@0: applyPattern(pattern, FALSE, parseError, status); michael@0: } michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: void michael@0: DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, UErrorCode& status) michael@0: { michael@0: UParseError parseError; michael@0: applyPattern(pattern, TRUE,parseError,status); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: void michael@0: DecimalFormat::applyLocalizedPattern(const UnicodeString& pattern, michael@0: UParseError& parseError, michael@0: UErrorCode& status) michael@0: { michael@0: applyPattern(pattern, TRUE,parseError,status); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: void michael@0: DecimalFormat::applyPatternWithoutExpandAffix(const UnicodeString& pattern, michael@0: UBool localized, michael@0: UParseError& parseError, michael@0: UErrorCode& status) michael@0: { michael@0: if (U_FAILURE(status)) michael@0: { michael@0: return; michael@0: } michael@0: // Clear error struct michael@0: parseError.offset = -1; michael@0: parseError.preContext[0] = parseError.postContext[0] = (UChar)0; michael@0: michael@0: // Set the significant pattern symbols michael@0: UChar32 zeroDigit = kPatternZeroDigit; // '0' michael@0: UChar32 sigDigit = kPatternSignificantDigit; // '@' michael@0: UnicodeString groupingSeparator ((UChar)kPatternGroupingSeparator); michael@0: UnicodeString decimalSeparator ((UChar)kPatternDecimalSeparator); michael@0: UnicodeString percent ((UChar)kPatternPercent); michael@0: UnicodeString perMill ((UChar)kPatternPerMill); michael@0: UnicodeString digit ((UChar)kPatternDigit); // '#' michael@0: UnicodeString separator ((UChar)kPatternSeparator); michael@0: UnicodeString exponent ((UChar)kPatternExponent); michael@0: UnicodeString plus ((UChar)kPatternPlus); michael@0: UnicodeString minus ((UChar)kPatternMinus); michael@0: UnicodeString padEscape ((UChar)kPatternPadEscape); michael@0: // Substitute with the localized symbols if necessary michael@0: if (localized) { michael@0: zeroDigit = getConstSymbol(DecimalFormatSymbols::kZeroDigitSymbol).char32At(0); michael@0: sigDigit = getConstSymbol(DecimalFormatSymbols::kSignificantDigitSymbol).char32At(0); michael@0: groupingSeparator. remove().append(getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol)); michael@0: decimalSeparator. remove().append(getConstSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol)); michael@0: percent. remove().append(getConstSymbol(DecimalFormatSymbols::kPercentSymbol)); michael@0: perMill. remove().append(getConstSymbol(DecimalFormatSymbols::kPerMillSymbol)); michael@0: digit. remove().append(getConstSymbol(DecimalFormatSymbols::kDigitSymbol)); michael@0: separator. remove().append(getConstSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol)); michael@0: exponent. remove().append(getConstSymbol(DecimalFormatSymbols::kExponentialSymbol)); michael@0: plus. remove().append(getConstSymbol(DecimalFormatSymbols::kPlusSignSymbol)); michael@0: minus. remove().append(getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol)); michael@0: padEscape. remove().append(getConstSymbol(DecimalFormatSymbols::kPadEscapeSymbol)); michael@0: } michael@0: UChar nineDigit = (UChar)(zeroDigit + 9); michael@0: int32_t digitLen = digit.length(); michael@0: int32_t groupSepLen = groupingSeparator.length(); michael@0: int32_t decimalSepLen = decimalSeparator.length(); michael@0: michael@0: int32_t pos = 0; michael@0: int32_t patLen = pattern.length(); michael@0: // Part 0 is the positive pattern. Part 1, if present, is the negative michael@0: // pattern. michael@0: for (int32_t part=0; part<2 && pos 0 || sigDigitCount > 0) { michael@0: ++digitRightCount; michael@0: } else { michael@0: ++digitLeftCount; michael@0: } michael@0: if (groupingCount >= 0 && decimalPos < 0) { michael@0: ++groupingCount; michael@0: } michael@0: pos += digitLen; michael@0: } else if ((ch >= zeroDigit && ch <= nineDigit) || michael@0: ch == sigDigit) { michael@0: if (digitRightCount > 0) { michael@0: // Unexpected '0' michael@0: debug("Unexpected '0'") michael@0: status = U_UNEXPECTED_TOKEN; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: if (ch == sigDigit) { michael@0: ++sigDigitCount; michael@0: } else { michael@0: if (ch != zeroDigit && roundingPos < 0) { michael@0: roundingPos = digitLeftCount + zeroDigitCount; michael@0: } michael@0: if (roundingPos >= 0) { michael@0: roundingInc.append((char)(ch - zeroDigit + '0')); michael@0: } michael@0: ++zeroDigitCount; michael@0: } michael@0: if (groupingCount >= 0 && decimalPos < 0) { michael@0: ++groupingCount; michael@0: } michael@0: pos += U16_LENGTH(ch); michael@0: } else if (pattern.compare(pos, groupSepLen, groupingSeparator) == 0) { michael@0: if (decimalPos >= 0) { michael@0: // Grouping separator after decimal michael@0: debug("Grouping separator after decimal") michael@0: status = U_UNEXPECTED_TOKEN; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: groupingCount2 = groupingCount; michael@0: groupingCount = 0; michael@0: pos += groupSepLen; michael@0: } else if (pattern.compare(pos, decimalSepLen, decimalSeparator) == 0) { michael@0: if (decimalPos >= 0) { michael@0: // Multiple decimal separators michael@0: debug("Multiple decimal separators") michael@0: status = U_MULTIPLE_DECIMAL_SEPARATORS; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: // Intentionally incorporate the digitRightCount, michael@0: // even though it is illegal for this to be > 0 michael@0: // at this point. We check pattern syntax below. michael@0: decimalPos = digitLeftCount + zeroDigitCount + digitRightCount; michael@0: pos += decimalSepLen; michael@0: } else { michael@0: if (pattern.compare(pos, exponent.length(), exponent) == 0) { michael@0: if (expDigits >= 0) { michael@0: // Multiple exponential symbols michael@0: debug("Multiple exponential symbols") michael@0: status = U_MULTIPLE_EXPONENTIAL_SYMBOLS; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: if (groupingCount >= 0) { michael@0: // Grouping separator in exponential pattern michael@0: debug("Grouping separator in exponential pattern") michael@0: status = U_MALFORMED_EXPONENTIAL_PATTERN; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: pos += exponent.length(); michael@0: // Check for positive prefix michael@0: if (pos < patLen michael@0: && pattern.compare(pos, plus.length(), plus) == 0) { michael@0: expSignAlways = TRUE; michael@0: pos += plus.length(); michael@0: } michael@0: // Use lookahead to parse out the exponential part of the michael@0: // pattern, then jump into suffix subpart. michael@0: expDigits = 0; michael@0: while (pos < patLen && michael@0: pattern.char32At(pos) == zeroDigit) { michael@0: ++expDigits; michael@0: pos += U16_LENGTH(zeroDigit); michael@0: } michael@0: michael@0: // 1. Require at least one mantissa pattern digit michael@0: // 2. Disallow "#+ @" in mantissa michael@0: // 3. Require at least one exponent pattern digit michael@0: if (((digitLeftCount + zeroDigitCount) < 1 && michael@0: (sigDigitCount + digitRightCount) < 1) || michael@0: (sigDigitCount > 0 && digitLeftCount > 0) || michael@0: expDigits < 1) { michael@0: // Malformed exponential pattern michael@0: debug("Malformed exponential pattern") michael@0: status = U_MALFORMED_EXPONENTIAL_PATTERN; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: } michael@0: // Transition to suffix subpart michael@0: subpart = 2; // suffix subpart michael@0: affix = &suffix; michael@0: sub0Limit = pos; michael@0: continue; michael@0: } michael@0: break; michael@0: case 1: // Prefix subpart michael@0: case 2: // Suffix subpart michael@0: // Process the prefix / suffix characters michael@0: // Process unquoted characters seen in prefix or suffix michael@0: // subpart. michael@0: michael@0: // Several syntax characters implicitly begins the michael@0: // next subpart if we are in the prefix; otherwise michael@0: // they are illegal if unquoted. michael@0: if (!pattern.compare(pos, digitLen, digit) || michael@0: !pattern.compare(pos, groupSepLen, groupingSeparator) || michael@0: !pattern.compare(pos, decimalSepLen, decimalSeparator) || michael@0: (ch >= zeroDigit && ch <= nineDigit) || michael@0: ch == sigDigit) { michael@0: if (subpart == 1) { // prefix subpart michael@0: subpart = 0; // pattern proper subpart michael@0: sub0Start = pos; // Reprocess this character michael@0: continue; michael@0: } else { michael@0: status = U_UNQUOTED_SPECIAL; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: } else if (ch == kCurrencySign) { michael@0: affix->append(kQuote); // Encode currency michael@0: // Use lookahead to determine if the currency sign is michael@0: // doubled or not. michael@0: U_ASSERT(U16_LENGTH(kCurrencySign) == 1); michael@0: if ((pos+1) < pattern.length() && pattern[pos+1] == kCurrencySign) { michael@0: affix->append(kCurrencySign); michael@0: ++pos; // Skip over the doubled character michael@0: if ((pos+1) < pattern.length() && michael@0: pattern[pos+1] == kCurrencySign) { michael@0: affix->append(kCurrencySign); michael@0: ++pos; // Skip over the doubled character michael@0: fCurrencySignCount = fgCurrencySignCountInPluralFormat; michael@0: } else { michael@0: fCurrencySignCount = fgCurrencySignCountInISOFormat; michael@0: } michael@0: } else { michael@0: fCurrencySignCount = fgCurrencySignCountInSymbolFormat; michael@0: } michael@0: // Fall through to append(ch) michael@0: } else if (ch == kQuote) { michael@0: // A quote outside quotes indicates either the opening michael@0: // quote or two quotes, which is a quote literal. That is, michael@0: // we have the first quote in 'do' or o''clock. michael@0: U_ASSERT(U16_LENGTH(kQuote) == 1); michael@0: ++pos; michael@0: if (pos < pattern.length() && pattern[pos] == kQuote) { michael@0: affix->append(kQuote); // Encode quote michael@0: // Fall through to append(ch) michael@0: } else { michael@0: subpart += 2; // open quote michael@0: continue; michael@0: } michael@0: } else if (pattern.compare(pos, separator.length(), separator) == 0) { michael@0: // Don't allow separators in the prefix, and don't allow michael@0: // separators in the second pattern (part == 1). michael@0: if (subpart == 1 || part == 1) { michael@0: // Unexpected separator michael@0: debug("Unexpected separator") michael@0: status = U_UNEXPECTED_TOKEN; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: sub2Limit = pos; michael@0: isPartDone = TRUE; // Go to next part michael@0: pos += separator.length(); michael@0: break; michael@0: } else if (pattern.compare(pos, percent.length(), percent) == 0) { michael@0: // Next handle characters which are appended directly. michael@0: if (multiplier != 1) { michael@0: // Too many percent/perMill characters michael@0: debug("Too many percent characters") michael@0: status = U_MULTIPLE_PERCENT_SYMBOLS; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: affix->append(kQuote); // Encode percent/perMill michael@0: affix->append(kPatternPercent); // Use unlocalized pattern char michael@0: multiplier = 100; michael@0: pos += percent.length(); michael@0: break; michael@0: } else if (pattern.compare(pos, perMill.length(), perMill) == 0) { michael@0: // Next handle characters which are appended directly. michael@0: if (multiplier != 1) { michael@0: // Too many percent/perMill characters michael@0: debug("Too many perMill characters") michael@0: status = U_MULTIPLE_PERMILL_SYMBOLS; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: affix->append(kQuote); // Encode percent/perMill michael@0: affix->append(kPatternPerMill); // Use unlocalized pattern char michael@0: multiplier = 1000; michael@0: pos += perMill.length(); michael@0: break; michael@0: } else if (pattern.compare(pos, padEscape.length(), padEscape) == 0) { michael@0: if (padPos >= 0 || // Multiple pad specifiers michael@0: (pos+1) == pattern.length()) { // Nothing after padEscape michael@0: debug("Multiple pad specifiers") michael@0: status = U_MULTIPLE_PAD_SPECIFIERS; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: padPos = pos; michael@0: pos += padEscape.length(); michael@0: padChar = pattern.char32At(pos); michael@0: pos += U16_LENGTH(padChar); michael@0: break; michael@0: } else if (pattern.compare(pos, minus.length(), minus) == 0) { michael@0: affix->append(kQuote); // Encode minus michael@0: affix->append(kPatternMinus); michael@0: pos += minus.length(); michael@0: break; michael@0: } else if (pattern.compare(pos, plus.length(), plus) == 0) { michael@0: affix->append(kQuote); // Encode plus michael@0: affix->append(kPatternPlus); michael@0: pos += plus.length(); michael@0: break; michael@0: } michael@0: // Unquoted, non-special characters fall through to here, as michael@0: // well as other code which needs to append something to the michael@0: // affix. michael@0: affix->append(ch); michael@0: pos += U16_LENGTH(ch); michael@0: break; michael@0: case 3: // Prefix subpart, in quote michael@0: case 4: // Suffix subpart, in quote michael@0: // A quote within quotes indicates either the closing michael@0: // quote or two quotes, which is a quote literal. That is, michael@0: // we have the second quote in 'do' or 'don''t'. michael@0: if (ch == kQuote) { michael@0: ++pos; michael@0: if (pos < pattern.length() && pattern[pos] == kQuote) { michael@0: affix->append(kQuote); // Encode quote michael@0: // Fall through to append(ch) michael@0: } else { michael@0: subpart -= 2; // close quote michael@0: continue; michael@0: } michael@0: } michael@0: affix->append(ch); michael@0: pos += U16_LENGTH(ch); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (sub0Limit == 0) { michael@0: sub0Limit = pattern.length(); michael@0: } michael@0: michael@0: if (sub2Limit == 0) { michael@0: sub2Limit = pattern.length(); michael@0: } michael@0: michael@0: /* Handle patterns with no '0' pattern character. These patterns michael@0: * are legal, but must be recodified to make sense. "##.###" -> michael@0: * "#0.###". ".###" -> ".0##". michael@0: * michael@0: * We allow patterns of the form "####" to produce a zeroDigitCount michael@0: * of zero (got that?); although this seems like it might make it michael@0: * possible for format() to produce empty strings, format() checks michael@0: * for this condition and outputs a zero digit in this situation. michael@0: * Having a zeroDigitCount of zero yields a minimum integer digits michael@0: * of zero, which allows proper round-trip patterns. We don't want michael@0: * "#" to become "#0" when toPattern() is called (even though that's michael@0: * what it really is, semantically). michael@0: */ michael@0: if (zeroDigitCount == 0 && sigDigitCount == 0 && michael@0: digitLeftCount > 0 && decimalPos >= 0) { michael@0: // Handle "###.###" and "###." and ".###" michael@0: int n = decimalPos; michael@0: if (n == 0) michael@0: ++n; // Handle ".###" michael@0: digitRightCount = digitLeftCount - n; michael@0: digitLeftCount = n - 1; michael@0: zeroDigitCount = 1; michael@0: } michael@0: michael@0: // Do syntax checking on the digits, decimal points, and quotes. michael@0: if ((decimalPos < 0 && digitRightCount > 0 && sigDigitCount == 0) || michael@0: (decimalPos >= 0 && michael@0: (sigDigitCount > 0 || michael@0: decimalPos < digitLeftCount || michael@0: decimalPos > (digitLeftCount + zeroDigitCount))) || michael@0: groupingCount == 0 || groupingCount2 == 0 || michael@0: (sigDigitCount > 0 && zeroDigitCount > 0) || michael@0: subpart > 2) michael@0: { // subpart > 2 == unmatched quote michael@0: debug("Syntax error") michael@0: status = U_PATTERN_SYNTAX_ERROR; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: michael@0: // Make sure pad is at legal position before or after affix. michael@0: if (padPos >= 0) { michael@0: if (padPos == start) { michael@0: padPos = kPadBeforePrefix; michael@0: } else if (padPos+2 == sub0Start) { michael@0: padPos = kPadAfterPrefix; michael@0: } else if (padPos == sub0Limit) { michael@0: padPos = kPadBeforeSuffix; michael@0: } else if (padPos+2 == sub2Limit) { michael@0: padPos = kPadAfterSuffix; michael@0: } else { michael@0: // Illegal pad position michael@0: debug("Illegal pad position") michael@0: status = U_ILLEGAL_PAD_POSITION; michael@0: syntaxError(pattern,pos,parseError); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: if (part == 0) { michael@0: delete fPosPrefixPattern; michael@0: delete fPosSuffixPattern; michael@0: delete fNegPrefixPattern; michael@0: delete fNegSuffixPattern; michael@0: fPosPrefixPattern = new UnicodeString(prefix); michael@0: /* test for NULL */ michael@0: if (fPosPrefixPattern == 0) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: fPosSuffixPattern = new UnicodeString(suffix); michael@0: /* test for NULL */ michael@0: if (fPosSuffixPattern == 0) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: delete fPosPrefixPattern; michael@0: return; michael@0: } michael@0: fNegPrefixPattern = 0; michael@0: fNegSuffixPattern = 0; michael@0: michael@0: fUseExponentialNotation = (expDigits >= 0); michael@0: if (fUseExponentialNotation) { michael@0: fMinExponentDigits = expDigits; michael@0: } michael@0: fExponentSignAlwaysShown = expSignAlways; michael@0: int32_t digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount; michael@0: // The effectiveDecimalPos is the position the decimal is at or michael@0: // would be at if there is no decimal. Note that if michael@0: // decimalPos<0, then digitTotalCount == digitLeftCount + michael@0: // zeroDigitCount. michael@0: int32_t effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount; michael@0: UBool isSigDig = (sigDigitCount > 0); michael@0: setSignificantDigitsUsed(isSigDig); michael@0: if (isSigDig) { michael@0: setMinimumSignificantDigits(sigDigitCount); michael@0: setMaximumSignificantDigits(sigDigitCount + digitRightCount); michael@0: } else { michael@0: int32_t minInt = effectiveDecimalPos - digitLeftCount; michael@0: setMinimumIntegerDigits(minInt); michael@0: setMaximumIntegerDigits(fUseExponentialNotation michael@0: ? digitLeftCount + getMinimumIntegerDigits() michael@0: : NumberFormat::gDefaultMaxIntegerDigits); michael@0: setMaximumFractionDigits(decimalPos >= 0 michael@0: ? (digitTotalCount - decimalPos) : 0); michael@0: setMinimumFractionDigits(decimalPos >= 0 michael@0: ? (digitLeftCount + zeroDigitCount - decimalPos) : 0); michael@0: } michael@0: setGroupingUsed(groupingCount > 0); michael@0: fGroupingSize = (groupingCount > 0) ? groupingCount : 0; michael@0: fGroupingSize2 = (groupingCount2 > 0 && groupingCount2 != groupingCount) michael@0: ? groupingCount2 : 0; michael@0: setMultiplier(multiplier); michael@0: setDecimalSeparatorAlwaysShown(decimalPos == 0 michael@0: || decimalPos == digitTotalCount); michael@0: if (padPos >= 0) { michael@0: fPadPosition = (EPadPosition) padPos; michael@0: // To compute the format width, first set up sub0Limit - michael@0: // sub0Start. Add in prefix/suffix length later. michael@0: michael@0: // fFormatWidth = prefix.length() + suffix.length() + michael@0: // sub0Limit - sub0Start; michael@0: fFormatWidth = sub0Limit - sub0Start; michael@0: fPad = padChar; michael@0: } else { michael@0: fFormatWidth = 0; michael@0: } michael@0: if (roundingPos >= 0) { michael@0: roundingInc.setDecimalAt(effectiveDecimalPos - roundingPos); michael@0: if (fRoundingIncrement != NULL) { michael@0: *fRoundingIncrement = roundingInc; michael@0: } else { michael@0: fRoundingIncrement = new DigitList(roundingInc); michael@0: /* test for NULL */ michael@0: if (fRoundingIncrement == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: delete fPosPrefixPattern; michael@0: delete fPosSuffixPattern; michael@0: return; michael@0: } michael@0: } michael@0: fRoundingMode = kRoundHalfEven; michael@0: } else { michael@0: setRoundingIncrement(0.0); michael@0: } michael@0: } else { michael@0: fNegPrefixPattern = new UnicodeString(prefix); michael@0: /* test for NULL */ michael@0: if (fNegPrefixPattern == 0) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: fNegSuffixPattern = new UnicodeString(suffix); michael@0: /* test for NULL */ michael@0: if (fNegSuffixPattern == 0) { michael@0: delete fNegPrefixPattern; michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (pattern.length() == 0) { michael@0: delete fNegPrefixPattern; michael@0: delete fNegSuffixPattern; michael@0: fNegPrefixPattern = NULL; michael@0: fNegSuffixPattern = NULL; michael@0: if (fPosPrefixPattern != NULL) { michael@0: fPosPrefixPattern->remove(); michael@0: } else { michael@0: fPosPrefixPattern = new UnicodeString(); michael@0: /* test for NULL */ michael@0: if (fPosPrefixPattern == 0) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: } michael@0: if (fPosSuffixPattern != NULL) { michael@0: fPosSuffixPattern->remove(); michael@0: } else { michael@0: fPosSuffixPattern = new UnicodeString(); michael@0: /* test for NULL */ michael@0: if (fPosSuffixPattern == 0) { michael@0: delete fPosPrefixPattern; michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: } michael@0: michael@0: setMinimumIntegerDigits(0); michael@0: setMaximumIntegerDigits(kDoubleIntegerDigits); michael@0: setMinimumFractionDigits(0); michael@0: setMaximumFractionDigits(kDoubleFractionDigits); michael@0: michael@0: fUseExponentialNotation = FALSE; michael@0: fCurrencySignCount = fgCurrencySignCountZero; michael@0: setGroupingUsed(FALSE); michael@0: fGroupingSize = 0; michael@0: fGroupingSize2 = 0; michael@0: setMultiplier(1); michael@0: setDecimalSeparatorAlwaysShown(FALSE); michael@0: fFormatWidth = 0; michael@0: setRoundingIncrement(0.0); michael@0: } michael@0: michael@0: // If there was no negative pattern, or if the negative pattern is michael@0: // identical to the positive pattern, then prepend the minus sign to the michael@0: // positive pattern to form the negative pattern. michael@0: if (fNegPrefixPattern == NULL || michael@0: (*fNegPrefixPattern == *fPosPrefixPattern michael@0: && *fNegSuffixPattern == *fPosSuffixPattern)) { michael@0: _copy_ptr(&fNegSuffixPattern, fPosSuffixPattern); michael@0: if (fNegPrefixPattern == NULL) { michael@0: fNegPrefixPattern = new UnicodeString(); michael@0: /* test for NULL */ michael@0: if (fNegPrefixPattern == 0) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: } else { michael@0: fNegPrefixPattern->remove(); michael@0: } michael@0: fNegPrefixPattern->append(kQuote).append(kPatternMinus) michael@0: .append(*fPosPrefixPattern); michael@0: } michael@0: #ifdef FMT_DEBUG michael@0: UnicodeString s; michael@0: s.append((UnicodeString)"\"").append(pattern).append((UnicodeString)"\"->"); michael@0: debugout(s); michael@0: #endif michael@0: michael@0: // save the pattern michael@0: fFormatPattern = pattern; michael@0: } michael@0: michael@0: michael@0: void michael@0: DecimalFormat::expandAffixAdjustWidth(const UnicodeString* pluralCount) { michael@0: expandAffixes(pluralCount); michael@0: if (fFormatWidth > 0) { michael@0: // Finish computing format width (see above) michael@0: // TODO: how to handle fFormatWidth, michael@0: // need to save in f(Plural)AffixesForCurrecy? michael@0: fFormatWidth += fPositivePrefix.length() + fPositiveSuffix.length(); michael@0: } michael@0: } michael@0: michael@0: michael@0: void michael@0: DecimalFormat::applyPattern(const UnicodeString& pattern, michael@0: UBool localized, michael@0: UParseError& parseError, michael@0: UErrorCode& status) michael@0: { michael@0: // do the following re-set first. since they change private data by michael@0: // apply pattern again. michael@0: if (pattern.indexOf(kCurrencySign) != -1) { michael@0: if (fCurrencyPluralInfo == NULL) { michael@0: // initialize currencyPluralInfo if needed michael@0: fCurrencyPluralInfo = new CurrencyPluralInfo(fSymbols->getLocale(), status); michael@0: } michael@0: if (fAffixPatternsForCurrency == NULL) { michael@0: setupCurrencyAffixPatterns(status); michael@0: } michael@0: if (pattern.indexOf(fgTripleCurrencySign, 3, 0) != -1) { michael@0: // only setup the affixes of the current pattern. michael@0: setupCurrencyAffixes(pattern, TRUE, FALSE, status); michael@0: } michael@0: } michael@0: applyPatternWithoutExpandAffix(pattern, localized, parseError, status); michael@0: expandAffixAdjustWidth(NULL); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: michael@0: void michael@0: DecimalFormat::applyPatternInternally(const UnicodeString& pluralCount, michael@0: const UnicodeString& pattern, michael@0: UBool localized, michael@0: UParseError& parseError, michael@0: UErrorCode& status) { michael@0: applyPatternWithoutExpandAffix(pattern, localized, parseError, status); michael@0: expandAffixAdjustWidth(&pluralCount); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Sets the maximum number of digits allowed in the integer portion of a michael@0: * number. michael@0: * @see NumberFormat#setMaximumIntegerDigits michael@0: */ michael@0: void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { michael@0: NumberFormat::setMaximumIntegerDigits(_min(newValue, gDefaultMaxIntegerDigits)); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Sets the minimum number of digits allowed in the integer portion of a michael@0: * number. This override limits the integer digit count to 309. michael@0: * @see NumberFormat#setMinimumIntegerDigits michael@0: */ michael@0: void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { michael@0: NumberFormat::setMinimumIntegerDigits(_min(newValue, kDoubleIntegerDigits)); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Sets the maximum number of digits allowed in the fraction portion of a michael@0: * number. This override limits the fraction digit count to 340. michael@0: * @see NumberFormat#setMaximumFractionDigits michael@0: */ michael@0: void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { michael@0: NumberFormat::setMaximumFractionDigits(_min(newValue, kDoubleFractionDigits)); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Sets the minimum number of digits allowed in the fraction portion of a michael@0: * number. This override limits the fraction digit count to 340. michael@0: * @see NumberFormat#setMinimumFractionDigits michael@0: */ michael@0: void DecimalFormat::setMinimumFractionDigits(int32_t newValue) { michael@0: NumberFormat::setMinimumFractionDigits(_min(newValue, kDoubleFractionDigits)); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: int32_t DecimalFormat::getMinimumSignificantDigits() const { michael@0: return fMinSignificantDigits; michael@0: } michael@0: michael@0: int32_t DecimalFormat::getMaximumSignificantDigits() const { michael@0: return fMaxSignificantDigits; michael@0: } michael@0: michael@0: void DecimalFormat::setMinimumSignificantDigits(int32_t min) { michael@0: if (min < 1) { michael@0: min = 1; michael@0: } michael@0: // pin max sig dig to >= min michael@0: int32_t max = _max(fMaxSignificantDigits, min); michael@0: fMinSignificantDigits = min; michael@0: fMaxSignificantDigits = max; michael@0: fUseSignificantDigits = TRUE; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: void DecimalFormat::setMaximumSignificantDigits(int32_t max) { michael@0: if (max < 1) { michael@0: max = 1; michael@0: } michael@0: // pin min sig dig to 1..max michael@0: U_ASSERT(fMinSignificantDigits >= 1); michael@0: int32_t min = _min(fMinSignificantDigits, max); michael@0: fMinSignificantDigits = min; michael@0: fMaxSignificantDigits = max; michael@0: fUseSignificantDigits = TRUE; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: UBool DecimalFormat::areSignificantDigitsUsed() const { michael@0: return fUseSignificantDigits; michael@0: } michael@0: michael@0: void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) { michael@0: fUseSignificantDigits = useSignificantDigits; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: void DecimalFormat::setCurrencyInternally(const UChar* theCurrency, michael@0: UErrorCode& ec) { michael@0: // If we are a currency format, then modify our affixes to michael@0: // encode the currency symbol for the given currency in our michael@0: // locale, and adjust the decimal digits and rounding for the michael@0: // given currency. michael@0: michael@0: // Note: The code is ordered so that this object is *not changed* michael@0: // until we are sure we are going to succeed. michael@0: michael@0: // NULL or empty currency is *legal* and indicates no currency. michael@0: UBool isCurr = (theCurrency && *theCurrency); michael@0: michael@0: double rounding = 0.0; michael@0: int32_t frac = 0; michael@0: if (fCurrencySignCount != fgCurrencySignCountZero && isCurr) { michael@0: rounding = ucurr_getRoundingIncrement(theCurrency, &ec); michael@0: frac = ucurr_getDefaultFractionDigits(theCurrency, &ec); michael@0: } michael@0: michael@0: NumberFormat::setCurrency(theCurrency, ec); michael@0: if (U_FAILURE(ec)) return; michael@0: michael@0: if (fCurrencySignCount != fgCurrencySignCountZero) { michael@0: // NULL or empty currency is *legal* and indicates no currency. michael@0: if (isCurr) { michael@0: setRoundingIncrement(rounding); michael@0: setMinimumFractionDigits(frac); michael@0: setMaximumFractionDigits(frac); michael@0: } michael@0: expandAffixes(NULL); michael@0: } michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: void DecimalFormat::setCurrency(const UChar* theCurrency, UErrorCode& ec) { michael@0: // set the currency before compute affixes to get the right currency names michael@0: NumberFormat::setCurrency(theCurrency, ec); michael@0: if (fFormatPattern.indexOf(fgTripleCurrencySign, 3, 0) != -1) { michael@0: UnicodeString savedPtn = fFormatPattern; michael@0: setupCurrencyAffixes(fFormatPattern, TRUE, TRUE, ec); michael@0: UParseError parseErr; michael@0: applyPattern(savedPtn, FALSE, parseErr, ec); michael@0: } michael@0: // set the currency after apply pattern to get the correct rounding/fraction michael@0: setCurrencyInternally(theCurrency, ec); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: // Deprecated variant with no UErrorCode parameter michael@0: void DecimalFormat::setCurrency(const UChar* theCurrency) { michael@0: UErrorCode ec = U_ZERO_ERROR; michael@0: setCurrency(theCurrency, ec); michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: michael@0: void DecimalFormat::getEffectiveCurrency(UChar* result, UErrorCode& ec) const { michael@0: if (fSymbols == NULL) { michael@0: ec = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: ec = U_ZERO_ERROR; michael@0: const UChar* c = getCurrency(); michael@0: if (*c == 0) { michael@0: const UnicodeString &intl = michael@0: fSymbols->getConstSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); michael@0: c = intl.getBuffer(); // ok for intl to go out of scope michael@0: } michael@0: u_strncpy(result, c, 3); michael@0: result[3] = 0; michael@0: } michael@0: michael@0: /** michael@0: * Return the number of fraction digits to display, or the total michael@0: * number of digits for significant digit formats and exponential michael@0: * formats. michael@0: */ michael@0: int32_t michael@0: DecimalFormat::precision() const { michael@0: if (areSignificantDigitsUsed()) { michael@0: return getMaximumSignificantDigits(); michael@0: } else if (fUseExponentialNotation) { michael@0: return getMinimumIntegerDigits() + getMaximumFractionDigits(); michael@0: } else { michael@0: return getMaximumFractionDigits(); michael@0: } michael@0: } michael@0: michael@0: michael@0: // TODO: template algorithm michael@0: Hashtable* michael@0: DecimalFormat::initHashForAffix(UErrorCode& status) { michael@0: if ( U_FAILURE(status) ) { michael@0: return NULL; michael@0: } michael@0: Hashtable* hTable; michael@0: if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: if ( U_FAILURE(status) ) { michael@0: delete hTable; michael@0: return NULL; michael@0: } michael@0: hTable->setValueComparator(decimfmtAffixValueComparator); michael@0: return hTable; michael@0: } michael@0: michael@0: Hashtable* michael@0: DecimalFormat::initHashForAffixPattern(UErrorCode& status) { michael@0: if ( U_FAILURE(status) ) { michael@0: return NULL; michael@0: } michael@0: Hashtable* hTable; michael@0: if ( (hTable = new Hashtable(TRUE, status)) == NULL ) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: if ( U_FAILURE(status) ) { michael@0: delete hTable; michael@0: return NULL; michael@0: } michael@0: hTable->setValueComparator(decimfmtAffixPatternValueComparator); michael@0: return hTable; michael@0: } michael@0: michael@0: void michael@0: DecimalFormat::deleteHashForAffix(Hashtable*& table) michael@0: { michael@0: if ( table == NULL ) { michael@0: return; michael@0: } michael@0: int32_t pos = -1; michael@0: const UHashElement* element = NULL; michael@0: while ( (element = table->nextElement(pos)) != NULL ) { michael@0: const UHashTok valueTok = element->value; michael@0: const AffixesForCurrency* value = (AffixesForCurrency*)valueTok.pointer; michael@0: delete value; michael@0: } michael@0: delete table; michael@0: table = NULL; michael@0: } michael@0: michael@0: michael@0: michael@0: void michael@0: DecimalFormat::deleteHashForAffixPattern() michael@0: { michael@0: if ( fAffixPatternsForCurrency == NULL ) { michael@0: return; michael@0: } michael@0: int32_t pos = -1; michael@0: const UHashElement* element = NULL; michael@0: while ( (element = fAffixPatternsForCurrency->nextElement(pos)) != NULL ) { michael@0: const UHashTok valueTok = element->value; michael@0: const AffixPatternsForCurrency* value = (AffixPatternsForCurrency*)valueTok.pointer; michael@0: delete value; michael@0: } michael@0: delete fAffixPatternsForCurrency; michael@0: fAffixPatternsForCurrency = NULL; michael@0: } michael@0: michael@0: michael@0: void michael@0: DecimalFormat::copyHashForAffixPattern(const Hashtable* source, michael@0: Hashtable* target, michael@0: UErrorCode& status) { michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: int32_t pos = -1; michael@0: const UHashElement* element = NULL; michael@0: if ( source ) { michael@0: while ( (element = source->nextElement(pos)) != NULL ) { michael@0: const UHashTok keyTok = element->key; michael@0: const UnicodeString* key = (UnicodeString*)keyTok.pointer; michael@0: const UHashTok valueTok = element->value; michael@0: const AffixPatternsForCurrency* value = (AffixPatternsForCurrency*)valueTok.pointer; michael@0: AffixPatternsForCurrency* copy = new AffixPatternsForCurrency( michael@0: value->negPrefixPatternForCurrency, michael@0: value->negSuffixPatternForCurrency, michael@0: value->posPrefixPatternForCurrency, michael@0: value->posSuffixPatternForCurrency, michael@0: value->patternType); michael@0: target->put(UnicodeString(*key), copy, status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: DecimalFormat& DecimalFormat::setAttribute( UNumberFormatAttribute attr, michael@0: int32_t newValue, michael@0: UErrorCode &status) { michael@0: if(U_FAILURE(status)) return *this; michael@0: michael@0: switch(attr) { michael@0: case UNUM_LENIENT_PARSE: michael@0: setLenient(newValue!=0); michael@0: break; michael@0: michael@0: case UNUM_PARSE_INT_ONLY: michael@0: setParseIntegerOnly(newValue!=0); michael@0: break; michael@0: michael@0: case UNUM_GROUPING_USED: michael@0: setGroupingUsed(newValue!=0); michael@0: break; michael@0: michael@0: case UNUM_DECIMAL_ALWAYS_SHOWN: michael@0: setDecimalSeparatorAlwaysShown(newValue!=0); michael@0: break; michael@0: michael@0: case UNUM_MAX_INTEGER_DIGITS: michael@0: setMaximumIntegerDigits(newValue); michael@0: break; michael@0: michael@0: case UNUM_MIN_INTEGER_DIGITS: michael@0: setMinimumIntegerDigits(newValue); michael@0: break; michael@0: michael@0: case UNUM_INTEGER_DIGITS: michael@0: setMinimumIntegerDigits(newValue); michael@0: setMaximumIntegerDigits(newValue); michael@0: break; michael@0: michael@0: case UNUM_MAX_FRACTION_DIGITS: michael@0: setMaximumFractionDigits(newValue); michael@0: break; michael@0: michael@0: case UNUM_MIN_FRACTION_DIGITS: michael@0: setMinimumFractionDigits(newValue); michael@0: break; michael@0: michael@0: case UNUM_FRACTION_DIGITS: michael@0: setMinimumFractionDigits(newValue); michael@0: setMaximumFractionDigits(newValue); michael@0: break; michael@0: michael@0: case UNUM_SIGNIFICANT_DIGITS_USED: michael@0: setSignificantDigitsUsed(newValue!=0); michael@0: break; michael@0: michael@0: case UNUM_MAX_SIGNIFICANT_DIGITS: michael@0: setMaximumSignificantDigits(newValue); michael@0: break; michael@0: michael@0: case UNUM_MIN_SIGNIFICANT_DIGITS: michael@0: setMinimumSignificantDigits(newValue); michael@0: break; michael@0: michael@0: case UNUM_MULTIPLIER: michael@0: setMultiplier(newValue); michael@0: break; michael@0: michael@0: case UNUM_GROUPING_SIZE: michael@0: setGroupingSize(newValue); michael@0: break; michael@0: michael@0: case UNUM_ROUNDING_MODE: michael@0: setRoundingMode((DecimalFormat::ERoundingMode)newValue); michael@0: break; michael@0: michael@0: case UNUM_FORMAT_WIDTH: michael@0: setFormatWidth(newValue); michael@0: break; michael@0: michael@0: case UNUM_PADDING_POSITION: michael@0: /** The position at which padding will take place. */ michael@0: setPadPosition((DecimalFormat::EPadPosition)newValue); michael@0: break; michael@0: michael@0: case UNUM_SECONDARY_GROUPING_SIZE: michael@0: setSecondaryGroupingSize(newValue); michael@0: break; michael@0: michael@0: #if UCONFIG_HAVE_PARSEALLINPUT michael@0: case UNUM_PARSE_ALL_INPUT: michael@0: setParseAllInput((UNumberFormatAttributeValue)newValue); michael@0: break; michael@0: #endif michael@0: michael@0: /* These are stored in fBoolFlags */ michael@0: case UNUM_PARSE_NO_EXPONENT: michael@0: case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: michael@0: if(!fBoolFlags.isValidValue(newValue)) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: } else { michael@0: fBoolFlags.set(attr, newValue); michael@0: } michael@0: break; michael@0: michael@0: case UNUM_SCALE: michael@0: fScale = newValue; michael@0: break; michael@0: michael@0: default: michael@0: status = U_UNSUPPORTED_ERROR; michael@0: break; michael@0: } michael@0: return *this; michael@0: } michael@0: michael@0: int32_t DecimalFormat::getAttribute( UNumberFormatAttribute attr, michael@0: UErrorCode &status ) const { michael@0: if(U_FAILURE(status)) return -1; michael@0: switch(attr) { michael@0: case UNUM_LENIENT_PARSE: michael@0: return isLenient(); michael@0: michael@0: case UNUM_PARSE_INT_ONLY: michael@0: return isParseIntegerOnly(); michael@0: michael@0: case UNUM_GROUPING_USED: michael@0: return isGroupingUsed(); michael@0: michael@0: case UNUM_DECIMAL_ALWAYS_SHOWN: michael@0: return isDecimalSeparatorAlwaysShown(); michael@0: michael@0: case UNUM_MAX_INTEGER_DIGITS: michael@0: return getMaximumIntegerDigits(); michael@0: michael@0: case UNUM_MIN_INTEGER_DIGITS: michael@0: return getMinimumIntegerDigits(); michael@0: michael@0: case UNUM_INTEGER_DIGITS: michael@0: // TBD: what should this return? michael@0: return getMinimumIntegerDigits(); michael@0: michael@0: case UNUM_MAX_FRACTION_DIGITS: michael@0: return getMaximumFractionDigits(); michael@0: michael@0: case UNUM_MIN_FRACTION_DIGITS: michael@0: return getMinimumFractionDigits(); michael@0: michael@0: case UNUM_FRACTION_DIGITS: michael@0: // TBD: what should this return? michael@0: return getMinimumFractionDigits(); michael@0: michael@0: case UNUM_SIGNIFICANT_DIGITS_USED: michael@0: return areSignificantDigitsUsed(); michael@0: michael@0: case UNUM_MAX_SIGNIFICANT_DIGITS: michael@0: return getMaximumSignificantDigits(); michael@0: michael@0: case UNUM_MIN_SIGNIFICANT_DIGITS: michael@0: return getMinimumSignificantDigits(); michael@0: michael@0: case UNUM_MULTIPLIER: michael@0: return getMultiplier(); michael@0: michael@0: case UNUM_GROUPING_SIZE: michael@0: return getGroupingSize(); michael@0: michael@0: case UNUM_ROUNDING_MODE: michael@0: return getRoundingMode(); michael@0: michael@0: case UNUM_FORMAT_WIDTH: michael@0: return getFormatWidth(); michael@0: michael@0: case UNUM_PADDING_POSITION: michael@0: return getPadPosition(); michael@0: michael@0: case UNUM_SECONDARY_GROUPING_SIZE: michael@0: return getSecondaryGroupingSize(); michael@0: michael@0: /* These are stored in fBoolFlags */ michael@0: case UNUM_PARSE_NO_EXPONENT: michael@0: case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: michael@0: return fBoolFlags.get(attr); michael@0: michael@0: case UNUM_SCALE: michael@0: return fScale; michael@0: michael@0: default: michael@0: status = U_UNSUPPORTED_ERROR; michael@0: break; michael@0: } michael@0: michael@0: return -1; /* undefined */ michael@0: } michael@0: michael@0: #if UCONFIG_HAVE_PARSEALLINPUT michael@0: void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) { michael@0: fParseAllInput = value; michael@0: #if UCONFIG_FORMAT_FASTPATHS_49 michael@0: handleChanged(); michael@0: #endif michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: DecimalFormat::copyHashForAffix(const Hashtable* source, michael@0: Hashtable* target, michael@0: UErrorCode& status) { michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: int32_t pos = -1; michael@0: const UHashElement* element = NULL; michael@0: if ( source ) { michael@0: while ( (element = source->nextElement(pos)) != NULL ) { michael@0: const UHashTok keyTok = element->key; michael@0: const UnicodeString* key = (UnicodeString*)keyTok.pointer; michael@0: michael@0: const UHashTok valueTok = element->value; michael@0: const AffixesForCurrency* value = (AffixesForCurrency*)valueTok.pointer; michael@0: AffixesForCurrency* copy = new AffixesForCurrency( michael@0: value->negPrefixForCurrency, michael@0: value->negSuffixForCurrency, michael@0: value->posPrefixForCurrency, michael@0: value->posSuffixForCurrency); michael@0: target->put(UnicodeString(*key), copy, status); michael@0: if ( U_FAILURE(status) ) { michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: #endif /* #if !UCONFIG_NO_FORMATTING */ michael@0: michael@0: //eof