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