diff options
Diffstat (limited to 'deps/node/deps/icu-small/source/i18n/plurrule.cpp')
-rw-r--r-- | deps/node/deps/icu-small/source/i18n/plurrule.cpp | 1781 |
1 files changed, 0 insertions, 1781 deletions
diff --git a/deps/node/deps/icu-small/source/i18n/plurrule.cpp b/deps/node/deps/icu-small/source/i18n/plurrule.cpp deleted file mode 100644 index 3caa48a5..00000000 --- a/deps/node/deps/icu-small/source/i18n/plurrule.cpp +++ /dev/null @@ -1,1781 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html -/* -******************************************************************************* -* Copyright (C) 2007-2016, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -* -* File plurrule.cpp -*/ - -#include <math.h> -#include <stdio.h> - -#include "unicode/utypes.h" -#include "unicode/localpointer.h" -#include "unicode/plurrule.h" -#include "unicode/upluralrules.h" -#include "unicode/ures.h" -#include "unicode/numfmt.h" -#include "unicode/decimfmt.h" -#include "charstr.h" -#include "cmemory.h" -#include "cstring.h" -#include "hash.h" -#include "locutil.h" -#include "mutex.h" -#include "patternprops.h" -#include "plurrule_impl.h" -#include "putilimp.h" -#include "ucln_in.h" -#include "ustrfmt.h" -#include "uassert.h" -#include "uvectr32.h" -#include "sharedpluralrules.h" -#include "unifiedcache.h" -#include "number_decimalquantity.h" - -#if !UCONFIG_NO_FORMATTING - -U_NAMESPACE_BEGIN - -using namespace icu::pluralimpl; -using icu::number::impl::DecimalQuantity; - -static const UChar PLURAL_KEYWORD_OTHER[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,0}; -static const UChar PLURAL_DEFAULT_RULE[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,COLON,SPACE,LOW_N,0}; -static const UChar PK_IN[]={LOW_I,LOW_N,0}; -static const UChar PK_NOT[]={LOW_N,LOW_O,LOW_T,0}; -static const UChar PK_IS[]={LOW_I,LOW_S,0}; -static const UChar PK_MOD[]={LOW_M,LOW_O,LOW_D,0}; -static const UChar PK_AND[]={LOW_A,LOW_N,LOW_D,0}; -static const UChar PK_OR[]={LOW_O,LOW_R,0}; -static const UChar PK_VAR_N[]={LOW_N,0}; -static const UChar PK_VAR_I[]={LOW_I,0}; -static const UChar PK_VAR_F[]={LOW_F,0}; -static const UChar PK_VAR_T[]={LOW_T,0}; -static const UChar PK_VAR_V[]={LOW_V,0}; -static const UChar PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0}; -static const UChar PK_DECIMAL[]={LOW_D,LOW_E,LOW_C,LOW_I,LOW_M,LOW_A,LOW_L,0}; -static const UChar PK_INTEGER[]={LOW_I,LOW_N,LOW_T,LOW_E,LOW_G,LOW_E,LOW_R,0}; - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules) -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration) - -PluralRules::PluralRules(UErrorCode& /*status*/) -: UObject(), - mRules(nullptr), - mInternalStatus(U_ZERO_ERROR) -{ -} - -PluralRules::PluralRules(const PluralRules& other) -: UObject(other), - mRules(nullptr), - mInternalStatus(U_ZERO_ERROR) -{ - *this=other; -} - -PluralRules::~PluralRules() { - delete mRules; -} - -SharedPluralRules::~SharedPluralRules() { - delete ptr; -} - -PluralRules* -PluralRules::clone() const { - PluralRules* newObj = new PluralRules(*this); - // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr if - // the newly created object was not fully constructed properly (an error occurred). - if (newObj != nullptr && U_FAILURE(newObj->mInternalStatus)) { - delete newObj; - newObj = nullptr; - } - return newObj; -} - -PluralRules& -PluralRules::operator=(const PluralRules& other) { - if (this != &other) { - delete mRules; - mRules = nullptr; - mInternalStatus = other.mInternalStatus; - if (U_FAILURE(mInternalStatus)) { - // bail out early if the object we were copying from was already 'invalid'. - return *this; - } - if (other.mRules != nullptr) { - mRules = new RuleChain(*other.mRules); - if (mRules == nullptr) { - mInternalStatus = U_MEMORY_ALLOCATION_ERROR; - } - else if (U_FAILURE(mRules->fInternalStatus)) { - // If the RuleChain wasn't fully copied, then set our status to failure as well. - mInternalStatus = mRules->fInternalStatus; - } - } - } - return *this; -} - -StringEnumeration* PluralRules::getAvailableLocales(UErrorCode &status) { - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer<StringEnumeration> result(new PluralAvailableLocalesEnumeration(status), status); - if (U_FAILURE(status)) { - return nullptr; - } - return result.orphan(); -} - - -PluralRules* U_EXPORT2 -PluralRules::createRules(const UnicodeString& description, UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - PluralRuleParser parser; - LocalPointer<PluralRules> newRules(new PluralRules(status), status); - if (U_FAILURE(status)) { - return nullptr; - } - parser.parse(description, newRules.getAlias(), status); - if (U_FAILURE(status)) { - newRules.adoptInstead(nullptr); - } - return newRules.orphan(); -} - - -PluralRules* U_EXPORT2 -PluralRules::createDefaultRules(UErrorCode& status) { - return createRules(UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1), status); -} - -/******************************************************************************/ -/* Create PluralRules cache */ - -template<> U_I18N_API -const SharedPluralRules *LocaleCacheKey<SharedPluralRules>::createObject( - const void * /*unused*/, UErrorCode &status) const { - const char *localeId = fLoc.getName(); - LocalPointer<PluralRules> pr(PluralRules::internalForLocale(localeId, UPLURAL_TYPE_CARDINAL, status), status); - if (U_FAILURE(status)) { - return nullptr; - } - LocalPointer<SharedPluralRules> result(new SharedPluralRules(pr.getAlias()), status); - if (U_FAILURE(status)) { - return nullptr; - } - pr.orphan(); // result was successfully created so it nows pr. - result->addRef(); - return result.orphan(); -} - -/* end plural rules cache */ -/******************************************************************************/ - -const SharedPluralRules* U_EXPORT2 -PluralRules::createSharedInstance( - const Locale& locale, UPluralType type, UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - if (type != UPLURAL_TYPE_CARDINAL) { - status = U_UNSUPPORTED_ERROR; - return nullptr; - } - const SharedPluralRules *result = nullptr; - UnifiedCache::getByLocale(locale, result, status); - return result; -} - -PluralRules* U_EXPORT2 -PluralRules::forLocale(const Locale& locale, UErrorCode& status) { - return forLocale(locale, UPLURAL_TYPE_CARDINAL, status); -} - -PluralRules* U_EXPORT2 -PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) { - if (type != UPLURAL_TYPE_CARDINAL) { - return internalForLocale(locale, type, status); - } - const SharedPluralRules *shared = createSharedInstance( - locale, type, status); - if (U_FAILURE(status)) { - return nullptr; - } - PluralRules *result = (*shared)->clone(); - shared->removeRef(); - if (result == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return result; -} - -PluralRules* U_EXPORT2 -PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status) { - if (U_FAILURE(status)) { - return nullptr; - } - if (type >= UPLURAL_TYPE_COUNT) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return nullptr; - } - LocalPointer<PluralRules> newObj(new PluralRules(status), status); - if (U_FAILURE(status)) { - return nullptr; - } - UnicodeString locRule = newObj->getRuleFromResource(locale, type, status); - // TODO: which other errors, if any, should be returned? - if (locRule.length() == 0) { - // If an out-of-memory error occurred, then stop and report the failure. - if (status == U_MEMORY_ALLOCATION_ERROR) { - return nullptr; - } - // Locales with no specific rules (all numbers have the "other" category - // will return a U_MISSING_RESOURCE_ERROR at this point. This is not - // an error. - locRule = UnicodeString(PLURAL_DEFAULT_RULE); - status = U_ZERO_ERROR; - } - PluralRuleParser parser; - parser.parse(locRule, newObj.getAlias(), status); - // TODO: should rule parse errors be returned, or - // should we silently use default rules? - // Original impl used default rules. - // Ask the question to ICU Core. - - return newObj.orphan(); -} - -UnicodeString -PluralRules::select(int32_t number) const { - return select(FixedDecimal(number)); -} - -UnicodeString -PluralRules::select(double number) const { - return select(FixedDecimal(number)); -} - -UnicodeString -PluralRules::select(const IFixedDecimal &number) const { - if (mRules == nullptr) { - return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1); - } - else { - return mRules->select(number); - } -} - - - -StringEnumeration* -PluralRules::getKeywords(UErrorCode& status) const { - if (U_FAILURE(status)) { - return nullptr; - } - if (U_FAILURE(mInternalStatus)) { - status = mInternalStatus; - return nullptr; - } - LocalPointer<StringEnumeration> nameEnumerator(new PluralKeywordEnumeration(mRules, status), status); - if (U_FAILURE(status)) { - return nullptr; - } - return nameEnumerator.orphan(); -} - -double -PluralRules::getUniqueKeywordValue(const UnicodeString& /* keyword */) { - // Not Implemented. - return UPLRULES_NO_UNIQUE_VALUE; -} - -int32_t -PluralRules::getAllKeywordValues(const UnicodeString & /* keyword */, double * /* dest */, - int32_t /* destCapacity */, UErrorCode& error) { - error = U_UNSUPPORTED_ERROR; - return 0; -} - - -static double scaleForInt(double d) { - double scale = 1.0; - while (d != floor(d)) { - d = d * 10.0; - scale = scale * 10.0; - } - return scale; -} - -static int32_t -getSamplesFromString(const UnicodeString &samples, double *dest, - int32_t destCapacity, UErrorCode& status) { - int32_t sampleCount = 0; - int32_t sampleStartIdx = 0; - int32_t sampleEndIdx = 0; - - //std::string ss; // TODO: debugging. - // std::cout << "PluralRules::getSamples(), samples = \"" << samples.toUTF8String(ss) << "\"\n"; - for (sampleCount = 0; sampleCount < destCapacity && sampleStartIdx < samples.length(); ) { - sampleEndIdx = samples.indexOf(COMMA, sampleStartIdx); - if (sampleEndIdx == -1) { - sampleEndIdx = samples.length(); - } - const UnicodeString &sampleRange = samples.tempSubStringBetween(sampleStartIdx, sampleEndIdx); - // ss.erase(); - // std::cout << "PluralRules::getSamples(), samplesRange = \"" << sampleRange.toUTF8String(ss) << "\"\n"; - int32_t tildeIndex = sampleRange.indexOf(TILDE); - if (tildeIndex < 0) { - FixedDecimal fixed(sampleRange, status); - double sampleValue = fixed.source; - if (fixed.visibleDecimalDigitCount == 0 || sampleValue != floor(sampleValue)) { - dest[sampleCount++] = sampleValue; - } - } else { - - FixedDecimal fixedLo(sampleRange.tempSubStringBetween(0, tildeIndex), status); - FixedDecimal fixedHi(sampleRange.tempSubStringBetween(tildeIndex+1), status); - double rangeLo = fixedLo.source; - double rangeHi = fixedHi.source; - if (U_FAILURE(status)) { - break; - } - if (rangeHi < rangeLo) { - status = U_INVALID_FORMAT_ERROR; - break; - } - - // For ranges of samples with fraction decimal digits, scale the number up so that we - // are adding one in the units place. Avoids roundoffs from repetitive adds of tenths. - - double scale = scaleForInt(rangeLo); - double t = scaleForInt(rangeHi); - if (t > scale) { - scale = t; - } - rangeLo *= scale; - rangeHi *= scale; - for (double n=rangeLo; n<=rangeHi; n+=1) { - // Hack Alert: don't return any decimal samples with integer values that - // originated from a format with trailing decimals. - // This API is returning doubles, which can't distinguish having displayed - // zeros to the right of the decimal. - // This results in test failures with values mapping back to a different keyword. - double sampleValue = n/scale; - if (!(sampleValue == floor(sampleValue) && fixedLo.visibleDecimalDigitCount > 0)) { - dest[sampleCount++] = sampleValue; - } - if (sampleCount >= destCapacity) { - break; - } - } - } - sampleStartIdx = sampleEndIdx + 1; - } - return sampleCount; -} - - -int32_t -PluralRules::getSamples(const UnicodeString &keyword, double *dest, - int32_t destCapacity, UErrorCode& status) { - if (destCapacity == 0 || U_FAILURE(status)) { - return 0; - } - if (U_FAILURE(mInternalStatus)) { - status = mInternalStatus; - return 0; - } - RuleChain *rc = rulesForKeyword(keyword); - if (rc == nullptr) { - return 0; - } - int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, dest, destCapacity, status); - if (numSamples == 0) { - numSamples = getSamplesFromString(rc->fDecimalSamples, dest, destCapacity, status); - } - return numSamples; -} - - -RuleChain *PluralRules::rulesForKeyword(const UnicodeString &keyword) const { - RuleChain *rc; - for (rc = mRules; rc != nullptr; rc = rc->fNext) { - if (rc->fKeyword == keyword) { - break; - } - } - return rc; -} - - -UBool -PluralRules::isKeyword(const UnicodeString& keyword) const { - if (0 == keyword.compare(PLURAL_KEYWORD_OTHER, 5)) { - return true; - } - return rulesForKeyword(keyword) != nullptr; -} - -UnicodeString -PluralRules::getKeywordOther() const { - return UnicodeString(TRUE, PLURAL_KEYWORD_OTHER, 5); -} - -UBool -PluralRules::operator==(const PluralRules& other) const { - const UnicodeString *ptrKeyword; - UErrorCode status= U_ZERO_ERROR; - - if ( this == &other ) { - return TRUE; - } - LocalPointer<StringEnumeration> myKeywordList(getKeywords(status)); - LocalPointer<StringEnumeration> otherKeywordList(other.getKeywords(status)); - if (U_FAILURE(status)) { - return FALSE; - } - - if (myKeywordList->count(status)!=otherKeywordList->count(status)) { - return FALSE; - } - myKeywordList->reset(status); - while ((ptrKeyword=myKeywordList->snext(status))!=nullptr) { - if (!other.isKeyword(*ptrKeyword)) { - return FALSE; - } - } - otherKeywordList->reset(status); - while ((ptrKeyword=otherKeywordList->snext(status))!=nullptr) { - if (!this->isKeyword(*ptrKeyword)) { - return FALSE; - } - } - if (U_FAILURE(status)) { - return FALSE; - } - - return TRUE; -} - - -void -PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - U_ASSERT(ruleIndex == 0); // Parsers are good for a single use only! - ruleSrc = &ruleData; - - while (ruleIndex< ruleSrc->length()) { - getNextToken(status); - if (U_FAILURE(status)) { - return; - } - checkSyntax(status); - if (U_FAILURE(status)) { - return; - } - switch (type) { - case tAnd: - U_ASSERT(curAndConstraint != nullptr); - curAndConstraint = curAndConstraint->add(status); - break; - case tOr: - { - U_ASSERT(currentChain != nullptr); - OrConstraint *orNode=currentChain->ruleHeader; - while (orNode->next != nullptr) { - orNode = orNode->next; - } - orNode->next= new OrConstraint(); - if (orNode->next == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - orNode=orNode->next; - orNode->next=nullptr; - curAndConstraint = orNode->add(status); - } - break; - case tIs: - U_ASSERT(curAndConstraint != nullptr); - U_ASSERT(curAndConstraint->value == -1); - U_ASSERT(curAndConstraint->rangeList == nullptr); - break; - case tNot: - U_ASSERT(curAndConstraint != nullptr); - curAndConstraint->negated=TRUE; - break; - - case tNotEqual: - curAndConstraint->negated=TRUE; - U_FALLTHROUGH; - case tIn: - case tWithin: - case tEqual: - { - U_ASSERT(curAndConstraint != nullptr); - LocalPointer<UVector32> newRangeList(new UVector32(status), status); - if (U_FAILURE(status)) { - break; - } - curAndConstraint->rangeList = newRangeList.orphan(); - curAndConstraint->rangeList->addElement(-1, status); // range Low - curAndConstraint->rangeList->addElement(-1, status); // range Hi - rangeLowIdx = 0; - rangeHiIdx = 1; - curAndConstraint->value=PLURAL_RANGE_HIGH; - curAndConstraint->integerOnly = (type != tWithin); - } - break; - case tNumber: - U_ASSERT(curAndConstraint != nullptr); - if ( (curAndConstraint->op==AndConstraint::MOD)&& - (curAndConstraint->opNum == -1 ) ) { - curAndConstraint->opNum=getNumberValue(token); - } - else { - if (curAndConstraint->rangeList == nullptr) { - // this is for an 'is' rule - curAndConstraint->value = getNumberValue(token); - } else { - // this is for an 'in' or 'within' rule - if (curAndConstraint->rangeList->elementAti(rangeLowIdx) == -1) { - curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeLowIdx); - curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx); - } - else { - curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx); - if (curAndConstraint->rangeList->elementAti(rangeLowIdx) > - curAndConstraint->rangeList->elementAti(rangeHiIdx)) { - // Range Lower bound > Range Upper bound. - // U_UNEXPECTED_TOKEN seems a little funny, but it is consistently - // used for all plural rule parse errors. - status = U_UNEXPECTED_TOKEN; - break; - } - } - } - } - break; - case tComma: - // TODO: rule syntax checking is inadequate, can happen with badly formed rules. - // Catch cases like "n mod 10, is 1" here instead. - if (curAndConstraint == nullptr || curAndConstraint->rangeList == nullptr) { - status = U_UNEXPECTED_TOKEN; - break; - } - U_ASSERT(curAndConstraint->rangeList->size() >= 2); - rangeLowIdx = curAndConstraint->rangeList->size(); - curAndConstraint->rangeList->addElement(-1, status); // range Low - rangeHiIdx = curAndConstraint->rangeList->size(); - curAndConstraint->rangeList->addElement(-1, status); // range Hi - break; - case tMod: - U_ASSERT(curAndConstraint != nullptr); - curAndConstraint->op=AndConstraint::MOD; - break; - case tVariableN: - case tVariableI: - case tVariableF: - case tVariableT: - case tVariableV: - U_ASSERT(curAndConstraint != nullptr); - curAndConstraint->digitsType = type; - break; - case tKeyword: - { - RuleChain *newChain = new RuleChain; - if (newChain == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - newChain->fKeyword = token; - if (prules->mRules == nullptr) { - prules->mRules = newChain; - } else { - // The new rule chain goes at the end of the linked list of rule chains, - // unless there is an "other" keyword & chain. "other" must remain last. - RuleChain *insertAfter = prules->mRules; - while (insertAfter->fNext!=nullptr && - insertAfter->fNext->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5) != 0 ){ - insertAfter=insertAfter->fNext; - } - newChain->fNext = insertAfter->fNext; - insertAfter->fNext = newChain; - } - OrConstraint *orNode = new OrConstraint(); - if (orNode == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - break; - } - newChain->ruleHeader = orNode; - curAndConstraint = orNode->add(status); - currentChain = newChain; - } - break; - - case tInteger: - for (;;) { - getNextToken(status); - if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) { - break; - } - if (type == tEllipsis) { - currentChain->fIntegerSamplesUnbounded = TRUE; - continue; - } - currentChain->fIntegerSamples.append(token); - } - break; - - case tDecimal: - for (;;) { - getNextToken(status); - if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) { - break; - } - if (type == tEllipsis) { - currentChain->fDecimalSamplesUnbounded = TRUE; - continue; - } - currentChain->fDecimalSamples.append(token); - } - break; - - default: - break; - } - prevType=type; - if (U_FAILURE(status)) { - break; - } - } -} - -UnicodeString -PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorCode& errCode) { - UnicodeString emptyStr; - - if (U_FAILURE(errCode)) { - return emptyStr; - } - LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "plurals", &errCode)); - if(U_FAILURE(errCode)) { - return emptyStr; - } - const char *typeKey; - switch (type) { - case UPLURAL_TYPE_CARDINAL: - typeKey = "locales"; - break; - case UPLURAL_TYPE_ORDINAL: - typeKey = "locales_ordinals"; - break; - default: - // Must not occur: The caller should have checked for valid types. - errCode = U_ILLEGAL_ARGUMENT_ERROR; - return emptyStr; - } - LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), typeKey, nullptr, &errCode)); - if(U_FAILURE(errCode)) { - return emptyStr; - } - int32_t resLen=0; - const char *curLocaleName=locale.getName(); - const UChar* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &errCode); - - if (s == nullptr) { - // Check parent locales. - UErrorCode status = U_ZERO_ERROR; - char parentLocaleName[ULOC_FULLNAME_CAPACITY]; - const char *curLocaleName2=locale.getName(); - uprv_strcpy(parentLocaleName, curLocaleName2); - - while (uloc_getParent(parentLocaleName, parentLocaleName, - ULOC_FULLNAME_CAPACITY, &status) > 0) { - resLen=0; - s = ures_getStringByKey(locRes.getAlias(), parentLocaleName, &resLen, &status); - if (s != nullptr) { - errCode = U_ZERO_ERROR; - break; - } - status = U_ZERO_ERROR; - } - } - if (s==nullptr) { - return emptyStr; - } - - char setKey[256]; - u_UCharsToChars(s, setKey, resLen + 1); - // printf("\n PluralRule: %s\n", setKey); - - LocalUResourceBundlePointer ruleRes(ures_getByKey(rb.getAlias(), "rules", nullptr, &errCode)); - if(U_FAILURE(errCode)) { - return emptyStr; - } - LocalUResourceBundlePointer setRes(ures_getByKey(ruleRes.getAlias(), setKey, nullptr, &errCode)); - if (U_FAILURE(errCode)) { - return emptyStr; - } - - int32_t numberKeys = ures_getSize(setRes.getAlias()); - UnicodeString result; - const char *key=nullptr; - for(int32_t i=0; i<numberKeys; ++i) { // Keys are zero, one, few, ... - UnicodeString rules = ures_getNextUnicodeString(setRes.getAlias(), &key, &errCode); - UnicodeString uKey(key, -1, US_INV); - result.append(uKey); - result.append(COLON); - result.append(rules); - result.append(SEMI_COLON); - } - return result; -} - - -UnicodeString -PluralRules::getRules() const { - UnicodeString rules; - if (mRules != nullptr) { - mRules->dumpRules(rules); - } - return rules; -} - -AndConstraint::AndConstraint(const AndConstraint& other) { - this->fInternalStatus = other.fInternalStatus; - if (U_FAILURE(fInternalStatus)) { - return; // stop early if the object we are copying from is invalid. - } - this->op = other.op; - this->opNum=other.opNum; - this->value=other.value; - if (other.rangeList != nullptr) { - LocalPointer<UVector32> newRangeList(new UVector32(fInternalStatus), fInternalStatus); - if (U_FAILURE(fInternalStatus)) { - return; - } - this->rangeList = newRangeList.orphan(); - this->rangeList->assign(*other.rangeList, fInternalStatus); - } - this->integerOnly=other.integerOnly; - this->negated=other.negated; - this->digitsType = other.digitsType; - if (other.next != nullptr) { - this->next = new AndConstraint(*other.next); - if (this->next == nullptr) { - fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - } - } -} - -AndConstraint::~AndConstraint() { - delete rangeList; - rangeList = nullptr; - delete next; - next = nullptr; -} - -UBool -AndConstraint::isFulfilled(const IFixedDecimal &number) { - UBool result = TRUE; - if (digitsType == none) { - // An empty AndConstraint, created by a rule with a keyword but no following expression. - return TRUE; - } - - PluralOperand operand = tokenTypeToPluralOperand(digitsType); - double n = number.getPluralOperand(operand); // pulls n | i | v | f value for the number. - // Will always be positive. - // May be non-integer (n option only) - do { - if (integerOnly && n != uprv_floor(n)) { - result = FALSE; - break; - } - - if (op == MOD) { - n = fmod(n, opNum); - } - if (rangeList == nullptr) { - result = value == -1 || // empty rule - n == value; // 'is' rule - break; - } - result = FALSE; // 'in' or 'within' rule - for (int32_t r=0; r<rangeList->size(); r+=2) { - if (rangeList->elementAti(r) <= n && n <= rangeList->elementAti(r+1)) { - result = TRUE; - break; - } - } - } while (FALSE); - - if (negated) { - result = !result; - } - return result; -} - -AndConstraint* -AndConstraint::add(UErrorCode& status) { - if (U_FAILURE(fInternalStatus)) { - status = fInternalStatus; - return nullptr; - } - this->next = new AndConstraint(); - if (this->next == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - return this->next; -} - - -OrConstraint::OrConstraint(const OrConstraint& other) { - this->fInternalStatus = other.fInternalStatus; - if (U_FAILURE(fInternalStatus)) { - return; // stop early if the object we are copying from is invalid. - } - if ( other.childNode != nullptr ) { - this->childNode = new AndConstraint(*(other.childNode)); - if (this->childNode == nullptr) { - fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - } - if (other.next != nullptr ) { - this->next = new OrConstraint(*(other.next)); - if (this->next == nullptr) { - fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - return; - } - if (U_FAILURE(this->next->fInternalStatus)) { - this->fInternalStatus = this->next->fInternalStatus; - } - } -} - -OrConstraint::~OrConstraint() { - delete childNode; - childNode = nullptr; - delete next; - next = nullptr; -} - -AndConstraint* -OrConstraint::add(UErrorCode& status) { - if (U_FAILURE(fInternalStatus)) { - status = fInternalStatus; - return nullptr; - } - OrConstraint *curOrConstraint=this; - { - while (curOrConstraint->next!=nullptr) { - curOrConstraint = curOrConstraint->next; - } - U_ASSERT(curOrConstraint->childNode == nullptr); - curOrConstraint->childNode = new AndConstraint(); - if (curOrConstraint->childNode == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - } - } - return curOrConstraint->childNode; -} - -UBool -OrConstraint::isFulfilled(const IFixedDecimal &number) { - OrConstraint* orRule=this; - UBool result=FALSE; - - while (orRule!=nullptr && !result) { - result=TRUE; - AndConstraint* andRule = orRule->childNode; - while (andRule!=nullptr && result) { - result = andRule->isFulfilled(number); - andRule=andRule->next; - } - orRule = orRule->next; - } - - return result; -} - - -RuleChain::RuleChain(const RuleChain& other) : - fKeyword(other.fKeyword), fDecimalSamples(other.fDecimalSamples), - fIntegerSamples(other.fIntegerSamples), fDecimalSamplesUnbounded(other.fDecimalSamplesUnbounded), - fIntegerSamplesUnbounded(other.fIntegerSamplesUnbounded), fInternalStatus(other.fInternalStatus) { - if (U_FAILURE(this->fInternalStatus)) { - return; // stop early if the object we are copying from is invalid. - } - if (other.ruleHeader != nullptr) { - this->ruleHeader = new OrConstraint(*(other.ruleHeader)); - if (this->ruleHeader == nullptr) { - this->fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - } - else if (U_FAILURE(this->ruleHeader->fInternalStatus)) { - // If the OrConstraint wasn't fully copied, then set our status to failure as well. - this->fInternalStatus = this->ruleHeader->fInternalStatus; - return; // exit early. - } - } - if (other.fNext != nullptr ) { - this->fNext = new RuleChain(*other.fNext); - if (this->fNext == nullptr) { - this->fInternalStatus = U_MEMORY_ALLOCATION_ERROR; - } - else if (U_FAILURE(this->fNext->fInternalStatus)) { - // If the RuleChain wasn't fully copied, then set our status to failure as well. - this->fInternalStatus = this->fNext->fInternalStatus; - } - } -} - -RuleChain::~RuleChain() { - delete fNext; - delete ruleHeader; -} - -UnicodeString -RuleChain::select(const IFixedDecimal &number) const { - if (!number.isNaN() && !number.isInfinite()) { - for (const RuleChain *rules = this; rules != nullptr; rules = rules->fNext) { - if (rules->ruleHeader->isFulfilled(number)) { - return rules->fKeyword; - } - } - } - return UnicodeString(TRUE, PLURAL_KEYWORD_OTHER, 5); -} - -static UnicodeString tokenString(tokenType tok) { - UnicodeString s; - switch (tok) { - case tVariableN: - s.append(LOW_N); break; - case tVariableI: - s.append(LOW_I); break; - case tVariableF: - s.append(LOW_F); break; - case tVariableV: - s.append(LOW_V); break; - case tVariableT: - s.append(LOW_T); break; - default: - s.append(TILDE); - } - return s; -} - -void -RuleChain::dumpRules(UnicodeString& result) { - UChar digitString[16]; - - if ( ruleHeader != nullptr ) { - result += fKeyword; - result += COLON; - result += SPACE; - OrConstraint* orRule=ruleHeader; - while ( orRule != nullptr ) { - AndConstraint* andRule=orRule->childNode; - while ( andRule != nullptr ) { - if ((andRule->op==AndConstraint::NONE) && (andRule->rangeList==nullptr) && (andRule->value == -1)) { - // Empty Rules. - } else if ( (andRule->op==AndConstraint::NONE) && (andRule->rangeList==nullptr) ) { - result += tokenString(andRule->digitsType); - result += UNICODE_STRING_SIMPLE(" is "); - if (andRule->negated) { - result += UNICODE_STRING_SIMPLE("not "); - } - uprv_itou(digitString,16, andRule->value,10,0); - result += UnicodeString(digitString); - } - else { - result += tokenString(andRule->digitsType); - result += SPACE; - if (andRule->op==AndConstraint::MOD) { - result += UNICODE_STRING_SIMPLE("mod "); - uprv_itou(digitString,16, andRule->opNum,10,0); - result += UnicodeString(digitString); - } - if (andRule->rangeList==nullptr) { - if (andRule->negated) { - result += UNICODE_STRING_SIMPLE(" is not "); - uprv_itou(digitString,16, andRule->value,10,0); - result += UnicodeString(digitString); - } - else { - result += UNICODE_STRING_SIMPLE(" is "); - uprv_itou(digitString,16, andRule->value,10,0); - result += UnicodeString(digitString); - } - } - else { - if (andRule->negated) { - if ( andRule->integerOnly ) { - result += UNICODE_STRING_SIMPLE(" not in "); - } - else { - result += UNICODE_STRING_SIMPLE(" not within "); - } - } - else { - if ( andRule->integerOnly ) { - result += UNICODE_STRING_SIMPLE(" in "); - } - else { - result += UNICODE_STRING_SIMPLE(" within "); - } - } - for (int32_t r=0; r<andRule->rangeList->size(); r+=2) { - int32_t rangeLo = andRule->rangeList->elementAti(r); - int32_t rangeHi = andRule->rangeList->elementAti(r+1); - uprv_itou(digitString,16, rangeLo, 10, 0); - result += UnicodeString(digitString); - result += UNICODE_STRING_SIMPLE(".."); - uprv_itou(digitString,16, rangeHi, 10,0); - result += UnicodeString(digitString); - if (r+2 < andRule->rangeList->size()) { - result += UNICODE_STRING_SIMPLE(", "); - } - } - } - } - if ( (andRule=andRule->next) != nullptr) { - result += UNICODE_STRING_SIMPLE(" and "); - } - } - if ( (orRule = orRule->next) != nullptr ) { - result += UNICODE_STRING_SIMPLE(" or "); - } - } - } - if ( fNext != nullptr ) { - result += UNICODE_STRING_SIMPLE("; "); - fNext->dumpRules(result); - } -} - - -UErrorCode -RuleChain::getKeywords(int32_t capacityOfKeywords, UnicodeString* keywords, int32_t& arraySize) const { - if (U_FAILURE(fInternalStatus)) { - return fInternalStatus; - } - if ( arraySize < capacityOfKeywords-1 ) { - keywords[arraySize++]=fKeyword; - } - else { - return U_BUFFER_OVERFLOW_ERROR; - } - - if ( fNext != nullptr ) { - return fNext->getKeywords(capacityOfKeywords, keywords, arraySize); - } - else { - return U_ZERO_ERROR; - } -} - -UBool -RuleChain::isKeyword(const UnicodeString& keywordParam) const { - if ( fKeyword == keywordParam ) { - return TRUE; - } - - if ( fNext != nullptr ) { - return fNext->isKeyword(keywordParam); - } - else { - return FALSE; - } -} - - -PluralRuleParser::PluralRuleParser() : - ruleIndex(0), token(), type(none), prevType(none), - curAndConstraint(nullptr), currentChain(nullptr), rangeLowIdx(-1), rangeHiIdx(-1) -{ -} - -PluralRuleParser::~PluralRuleParser() { -} - - -int32_t -PluralRuleParser::getNumberValue(const UnicodeString& token) { - int32_t i; - char digits[128]; - - i = token.extract(0, token.length(), digits, UPRV_LENGTHOF(digits), US_INV); - digits[i]='\0'; - - return((int32_t)atoi(digits)); -} - - -void -PluralRuleParser::checkSyntax(UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - if (!(prevType==none || prevType==tSemiColon)) { - type = getKeyType(token, type); // Switch token type from tKeyword if we scanned a reserved word, - // and we are not at the start of a rule, where a - // keyword is expected. - } - - switch(prevType) { - case none: - case tSemiColon: - if (type!=tKeyword && type != tEOF) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tVariableN: - case tVariableI: - case tVariableF: - case tVariableT: - case tVariableV: - if (type != tIs && type != tMod && type != tIn && - type != tNot && type != tWithin && type != tEqual && type != tNotEqual) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tKeyword: - if (type != tColon) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tColon: - if (!(type == tVariableN || - type == tVariableI || - type == tVariableF || - type == tVariableT || - type == tVariableV || - type == tAt)) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tIs: - if ( type != tNumber && type != tNot) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tNot: - if (type != tNumber && type != tIn && type != tWithin) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tMod: - case tDot2: - case tIn: - case tWithin: - case tEqual: - case tNotEqual: - if (type != tNumber) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tAnd: - case tOr: - if ( type != tVariableN && - type != tVariableI && - type != tVariableF && - type != tVariableT && - type != tVariableV) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tComma: - if (type != tNumber) { - status = U_UNEXPECTED_TOKEN; - } - break; - case tNumber: - if (type != tDot2 && type != tSemiColon && type != tIs && type != tNot && - type != tIn && type != tEqual && type != tNotEqual && type != tWithin && - type != tAnd && type != tOr && type != tComma && type != tAt && - type != tEOF) - { - status = U_UNEXPECTED_TOKEN; - } - // TODO: a comma following a number that is not part of a range will be allowed. - // It's not the only case of this sort of thing. Parser needs a re-write. - break; - case tAt: - if (type != tDecimal && type != tInteger) { - status = U_UNEXPECTED_TOKEN; - } - break; - default: - status = U_UNEXPECTED_TOKEN; - break; - } -} - - -/* - * Scan the next token from the input rules. - * rules and returned token type are in the parser state variables. - */ -void -PluralRuleParser::getNextToken(UErrorCode &status) -{ - if (U_FAILURE(status)) { - return; - } - - UChar ch; - while (ruleIndex < ruleSrc->length()) { - ch = ruleSrc->charAt(ruleIndex); - type = charType(ch); - if (type != tSpace) { - break; - } - ++(ruleIndex); - } - if (ruleIndex >= ruleSrc->length()) { - type = tEOF; - return; - } - int32_t curIndex= ruleIndex; - - switch (type) { - case tColon: - case tSemiColon: - case tComma: - case tEllipsis: - case tTilde: // scanned '~' - case tAt: // scanned '@' - case tEqual: // scanned '=' - case tMod: // scanned '%' - // Single character tokens. - ++curIndex; - break; - - case tNotEqual: // scanned '!' - if (ruleSrc->charAt(curIndex+1) == EQUALS) { - curIndex += 2; - } else { - type = none; - curIndex += 1; - } - break; - - case tKeyword: - while (type == tKeyword && ++curIndex < ruleSrc->length()) { - ch = ruleSrc->charAt(curIndex); - type = charType(ch); - } - type = tKeyword; - break; - - case tNumber: - while (type == tNumber && ++curIndex < ruleSrc->length()) { - ch = ruleSrc->charAt(curIndex); - type = charType(ch); - } - type = tNumber; - break; - - case tDot: - // We could be looking at either ".." in a range, or "..." at the end of a sample. - if (curIndex+1 >= ruleSrc->length() || ruleSrc->charAt(curIndex+1) != DOT) { - ++curIndex; - break; // Single dot - } - if (curIndex+2 >= ruleSrc->length() || ruleSrc->charAt(curIndex+2) != DOT) { - curIndex += 2; - type = tDot2; - break; // double dot - } - type = tEllipsis; - curIndex += 3; - break; // triple dot - - default: - status = U_UNEXPECTED_TOKEN; - ++curIndex; - break; - } - - U_ASSERT(ruleIndex <= ruleSrc->length()); - U_ASSERT(curIndex <= ruleSrc->length()); - token=UnicodeString(*ruleSrc, ruleIndex, curIndex-ruleIndex); - ruleIndex = curIndex; -} - -tokenType -PluralRuleParser::charType(UChar ch) { - if ((ch>=U_ZERO) && (ch<=U_NINE)) { - return tNumber; - } - if (ch>=LOW_A && ch<=LOW_Z) { - return tKeyword; - } - switch (ch) { - case COLON: - return tColon; - case SPACE: - return tSpace; - case SEMI_COLON: - return tSemiColon; - case DOT: - return tDot; - case COMMA: - return tComma; - case EXCLAMATION: - return tNotEqual; - case EQUALS: - return tEqual; - case PERCENT_SIGN: - return tMod; - case AT: - return tAt; - case ELLIPSIS: - return tEllipsis; - case TILDE: - return tTilde; - default : - return none; - } -} - - -// Set token type for reserved words in the Plural Rule syntax. - -tokenType -PluralRuleParser::getKeyType(const UnicodeString &token, tokenType keyType) -{ - if (keyType != tKeyword) { - return keyType; - } - - if (0 == token.compare(PK_VAR_N, 1)) { - keyType = tVariableN; - } else if (0 == token.compare(PK_VAR_I, 1)) { - keyType = tVariableI; - } else if (0 == token.compare(PK_VAR_F, 1)) { - keyType = tVariableF; - } else if (0 == token.compare(PK_VAR_T, 1)) { - keyType = tVariableT; - } else if (0 == token.compare(PK_VAR_V, 1)) { - keyType = tVariableV; - } else if (0 == token.compare(PK_IS, 2)) { - keyType = tIs; - } else if (0 == token.compare(PK_AND, 3)) { - keyType = tAnd; - } else if (0 == token.compare(PK_IN, 2)) { - keyType = tIn; - } else if (0 == token.compare(PK_WITHIN, 6)) { - keyType = tWithin; - } else if (0 == token.compare(PK_NOT, 3)) { - keyType = tNot; - } else if (0 == token.compare(PK_MOD, 3)) { - keyType = tMod; - } else if (0 == token.compare(PK_OR, 2)) { - keyType = tOr; - } else if (0 == token.compare(PK_DECIMAL, 7)) { - keyType = tDecimal; - } else if (0 == token.compare(PK_INTEGER, 7)) { - keyType = tInteger; - } - return keyType; -} - - -PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status) - : pos(0), fKeywordNames(status) { - if (U_FAILURE(status)) { - return; - } - fKeywordNames.setDeleter(uprv_deleteUObject); - UBool addKeywordOther = TRUE; - RuleChain *node = header; - while (node != nullptr) { - auto newElem = new UnicodeString(node->fKeyword); - if (newElem == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fKeywordNames.addElement(newElem, status); - if (U_FAILURE(status)) { - delete newElem; - return; - } - if (0 == node->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5)) { - addKeywordOther = FALSE; - } - node = node->fNext; - } - - if (addKeywordOther) { - auto newElem = new UnicodeString(PLURAL_KEYWORD_OTHER); - if (newElem == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return; - } - fKeywordNames.addElement(newElem, status); - if (U_FAILURE(status)) { - delete newElem; - return; - } - } -} - -const UnicodeString* -PluralKeywordEnumeration::snext(UErrorCode& status) { - if (U_SUCCESS(status) && pos < fKeywordNames.size()) { - return (const UnicodeString*)fKeywordNames.elementAt(pos++); - } - return nullptr; -} - -void -PluralKeywordEnumeration::reset(UErrorCode& /*status*/) { - pos=0; -} - -int32_t -PluralKeywordEnumeration::count(UErrorCode& /*status*/) const { - return fKeywordNames.size(); -} - -PluralKeywordEnumeration::~PluralKeywordEnumeration() { -} - -PluralOperand tokenTypeToPluralOperand(tokenType tt) { - switch(tt) { - case tVariableN: - return PLURAL_OPERAND_N; - case tVariableI: - return PLURAL_OPERAND_I; - case tVariableF: - return PLURAL_OPERAND_F; - case tVariableV: - return PLURAL_OPERAND_V; - case tVariableT: - return PLURAL_OPERAND_T; - default: - U_ASSERT(FALSE); // unexpected. - return PLURAL_OPERAND_N; - } -} - -FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) { - init(n, v, f); - // check values. TODO make into unit test. - // - // long visiblePower = (int) Math.pow(10, v); - // if (decimalDigits > visiblePower) { - // throw new IllegalArgumentException(); - // } - // double fraction = intValue + (decimalDigits / (double) visiblePower); - // if (fraction != source) { - // double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source)); - // if (diff > 0.00000001d) { - // throw new IllegalArgumentException(); - // } - // } -} - -FixedDecimal::FixedDecimal(double n, int32_t v) { - // Ugly, but for samples we don't care. - init(n, v, getFractionalDigits(n, v)); -} - -FixedDecimal::FixedDecimal(double n) { - init(n); -} - -FixedDecimal::FixedDecimal() { - init(0, 0, 0); -} - - -// Create a FixedDecimal from a UnicodeString containing a number. -// Inefficient, but only used for samples, so simplicity trumps efficiency. - -FixedDecimal::FixedDecimal(const UnicodeString &num, UErrorCode &status) { - CharString cs; - cs.appendInvariantChars(num, status); - DecimalQuantity dl; - dl.setToDecNumber(cs.toStringPiece(), status); - if (U_FAILURE(status)) { - init(0, 0, 0); - return; - } - int32_t decimalPoint = num.indexOf(DOT); - double n = dl.toDouble(); - if (decimalPoint == -1) { - init(n, 0, 0); - } else { - int32_t v = num.length() - decimalPoint - 1; - init(n, v, getFractionalDigits(n, v)); - } -} - - -FixedDecimal::FixedDecimal(const FixedDecimal &other) { - source = other.source; - visibleDecimalDigitCount = other.visibleDecimalDigitCount; - decimalDigits = other.decimalDigits; - decimalDigitsWithoutTrailingZeros = other.decimalDigitsWithoutTrailingZeros; - intValue = other.intValue; - _hasIntegerValue = other._hasIntegerValue; - isNegative = other.isNegative; - _isNaN = other._isNaN; - _isInfinite = other._isInfinite; -} - -FixedDecimal::~FixedDecimal() = default; - - -void FixedDecimal::init(double n) { - int32_t numFractionDigits = decimals(n); - init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits)); -} - - -void FixedDecimal::init(double n, int32_t v, int64_t f) { - isNegative = n < 0.0; - source = fabs(n); - _isNaN = uprv_isNaN(source); - _isInfinite = uprv_isInfinite(source); - if (_isNaN || _isInfinite) { - v = 0; - f = 0; - intValue = 0; - _hasIntegerValue = FALSE; - } else { - intValue = (int64_t)source; - _hasIntegerValue = (source == intValue); - } - - visibleDecimalDigitCount = v; - decimalDigits = f; - if (f == 0) { - decimalDigitsWithoutTrailingZeros = 0; - } else { - int64_t fdwtz = f; - while ((fdwtz%10) == 0) { - fdwtz /= 10; - } - decimalDigitsWithoutTrailingZeros = fdwtz; - } -} - - -// Fast path only exact initialization. Return true if successful. -// Note: Do not multiply by 10 each time through loop, rounding cruft can build -// up that makes the check for an integer result fail. -// A single multiply of the original number works more reliably. -static int32_t p10[] = {1, 10, 100, 1000, 10000}; -UBool FixedDecimal::quickInit(double n) { - UBool success = FALSE; - n = fabs(n); - int32_t numFractionDigits; - for (numFractionDigits = 0; numFractionDigits <= 3; numFractionDigits++) { - double scaledN = n * p10[numFractionDigits]; - if (scaledN == floor(scaledN)) { - success = TRUE; - break; - } - } - if (success) { - init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits)); - } - return success; -} - - - -int32_t FixedDecimal::decimals(double n) { - // Count the number of decimal digits in the fraction part of the number, excluding trailing zeros. - // fastpath the common cases, integers or fractions with 3 or fewer digits - n = fabs(n); - for (int ndigits=0; ndigits<=3; ndigits++) { - double scaledN = n * p10[ndigits]; - if (scaledN == floor(scaledN)) { - return ndigits; - } - } - - // Slow path, convert with sprintf, parse converted output. - char buf[30] = {0}; - sprintf(buf, "%1.15e", n); - // formatted number looks like this: 1.234567890123457e-01 - int exponent = atoi(buf+18); - int numFractionDigits = 15; - for (int i=16; ; --i) { - if (buf[i] != '0') { - break; - } - --numFractionDigits; - } - numFractionDigits -= exponent; // Fraction part of fixed point representation. - return numFractionDigits; -} - - -// Get the fraction digits of a double, represented as an integer. -// v is the number of visible fraction digits in the displayed form of the number. -// Example: n = 1001.234, v = 6, result = 234000 -// TODO: need to think through how this is used in the plural rule context. -// This function can easily encounter integer overflow, -// and can easily return noise digits when the precision of a double is exceeded. - -int64_t FixedDecimal::getFractionalDigits(double n, int32_t v) { - if (v == 0 || n == floor(n) || uprv_isNaN(n) || uprv_isPositiveInfinity(n)) { - return 0; - } - n = fabs(n); - double fract = n - floor(n); - switch (v) { - case 1: return (int64_t)(fract*10.0 + 0.5); - case 2: return (int64_t)(fract*100.0 + 0.5); - case 3: return (int64_t)(fract*1000.0 + 0.5); - default: - double scaled = floor(fract * pow(10.0, (double)v) + 0.5); - if (scaled > U_INT64_MAX) { - return U_INT64_MAX; - } else { - return (int64_t)scaled; - } - } -} - - -void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits) { - int32_t numTrailingFractionZeros = minFractionDigits - visibleDecimalDigitCount; - if (numTrailingFractionZeros > 0) { - for (int32_t i=0; i<numTrailingFractionZeros; i++) { - // Do not let the decimalDigits value overflow if there are many trailing zeros. - // Limit the value to 18 digits, the most that a 64 bit int can fully represent. - if (decimalDigits >= 100000000000000000LL) { - break; - } - decimalDigits *= 10; - } - visibleDecimalDigitCount += numTrailingFractionZeros; - } -} - - -double FixedDecimal::getPluralOperand(PluralOperand operand) const { - switch(operand) { - case PLURAL_OPERAND_N: return source; - case PLURAL_OPERAND_I: return static_cast<double>(intValue); - case PLURAL_OPERAND_F: return static_cast<double>(decimalDigits); - case PLURAL_OPERAND_T: return static_cast<double>(decimalDigitsWithoutTrailingZeros); - case PLURAL_OPERAND_V: return visibleDecimalDigitCount; - default: - U_ASSERT(FALSE); // unexpected. - return source; - } -} - -bool FixedDecimal::isNaN() const { - return _isNaN; -} - -bool FixedDecimal::isInfinite() const { - return _isInfinite; -} - -bool FixedDecimal::hasIntegerValue() const { - return _hasIntegerValue; -} - -bool FixedDecimal::isNanOrInfinity() const { - return _isNaN || _isInfinite; -} - -int32_t FixedDecimal::getVisibleFractionDigitCount() const { - return visibleDecimalDigitCount; -} - - - -PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) { - fOpenStatus = status; - if (U_FAILURE(status)) { - return; - } - fOpenStatus = U_ZERO_ERROR; // clear any warnings. - LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "plurals", &fOpenStatus)); - fLocales = ures_getByKey(rb.getAlias(), "locales", nullptr, &fOpenStatus); -} - -PluralAvailableLocalesEnumeration::~PluralAvailableLocalesEnumeration() { - ures_close(fLocales); - ures_close(fRes); - fLocales = nullptr; - fRes = nullptr; -} - -const char *PluralAvailableLocalesEnumeration::next(int32_t *resultLength, UErrorCode &status) { - if (U_FAILURE(status)) { - return nullptr; - } - if (U_FAILURE(fOpenStatus)) { - status = fOpenStatus; - return nullptr; - } - fRes = ures_getNextResource(fLocales, fRes, &status); - if (fRes == nullptr || U_FAILURE(status)) { - if (status == U_INDEX_OUTOFBOUNDS_ERROR) { - status = U_ZERO_ERROR; - } - return nullptr; - } - const char *result = ures_getKey(fRes); - if (resultLength != nullptr) { - *resultLength = static_cast<int32_t>(uprv_strlen(result)); - } - return result; -} - - -void PluralAvailableLocalesEnumeration::reset(UErrorCode &status) { - if (U_FAILURE(status)) { - return; - } - if (U_FAILURE(fOpenStatus)) { - status = fOpenStatus; - return; - } - ures_resetIterator(fLocales); -} - -int32_t PluralAvailableLocalesEnumeration::count(UErrorCode &status) const { - if (U_FAILURE(status)) { - return 0; - } - if (U_FAILURE(fOpenStatus)) { - status = fOpenStatus; - return 0; - } - return ures_getSize(fLocales); -} - -U_NAMESPACE_END - - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof |