1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/i18n/plurrule.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1596 @@ 1.4 +/* 1.5 +******************************************************************************* 1.6 +* Copyright (C) 2007-2013, International Business Machines Corporation and 1.7 +* others. All Rights Reserved. 1.8 +******************************************************************************* 1.9 +* 1.10 +* File plurrule.cpp 1.11 +*/ 1.12 + 1.13 +#include <math.h> 1.14 +#include <stdio.h> 1.15 + 1.16 +#include "unicode/utypes.h" 1.17 +#include "unicode/localpointer.h" 1.18 +#include "unicode/plurrule.h" 1.19 +#include "unicode/upluralrules.h" 1.20 +#include "unicode/ures.h" 1.21 +#include "charstr.h" 1.22 +#include "cmemory.h" 1.23 +#include "cstring.h" 1.24 +#include "digitlst.h" 1.25 +#include "hash.h" 1.26 +#include "locutil.h" 1.27 +#include "mutex.h" 1.28 +#include "patternprops.h" 1.29 +#include "plurrule_impl.h" 1.30 +#include "putilimp.h" 1.31 +#include "ucln_in.h" 1.32 +#include "ustrfmt.h" 1.33 +#include "uassert.h" 1.34 +#include "uvectr32.h" 1.35 + 1.36 +#if !UCONFIG_NO_FORMATTING 1.37 + 1.38 +U_NAMESPACE_BEGIN 1.39 + 1.40 +#define ARRAY_SIZE(array) (int32_t)(sizeof array / sizeof array[0]) 1.41 + 1.42 +static const UChar PLURAL_KEYWORD_OTHER[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,0}; 1.43 +static const UChar PLURAL_DEFAULT_RULE[]={LOW_O,LOW_T,LOW_H,LOW_E,LOW_R,COLON,SPACE,LOW_N,0}; 1.44 +static const UChar PK_IN[]={LOW_I,LOW_N,0}; 1.45 +static const UChar PK_NOT[]={LOW_N,LOW_O,LOW_T,0}; 1.46 +static const UChar PK_IS[]={LOW_I,LOW_S,0}; 1.47 +static const UChar PK_MOD[]={LOW_M,LOW_O,LOW_D,0}; 1.48 +static const UChar PK_AND[]={LOW_A,LOW_N,LOW_D,0}; 1.49 +static const UChar PK_OR[]={LOW_O,LOW_R,0}; 1.50 +static const UChar PK_VAR_N[]={LOW_N,0}; 1.51 +static const UChar PK_VAR_I[]={LOW_I,0}; 1.52 +static const UChar PK_VAR_F[]={LOW_F,0}; 1.53 +static const UChar PK_VAR_T[]={LOW_T,0}; 1.54 +static const UChar PK_VAR_V[]={LOW_V,0}; 1.55 +static const UChar PK_VAR_J[]={LOW_J,0}; 1.56 +static const UChar PK_WITHIN[]={LOW_W,LOW_I,LOW_T,LOW_H,LOW_I,LOW_N,0}; 1.57 +static const UChar PK_DECIMAL[]={LOW_D,LOW_E,LOW_C,LOW_I,LOW_M,LOW_A,LOW_L,0}; 1.58 +static const UChar PK_INTEGER[]={LOW_I,LOW_N,LOW_T,LOW_E,LOW_G,LOW_E,LOW_R,0}; 1.59 + 1.60 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralRules) 1.61 +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration) 1.62 + 1.63 +PluralRules::PluralRules(UErrorCode& /*status*/) 1.64 +: UObject(), 1.65 + mRules(NULL) 1.66 +{ 1.67 +} 1.68 + 1.69 +PluralRules::PluralRules(const PluralRules& other) 1.70 +: UObject(other), 1.71 + mRules(NULL) 1.72 +{ 1.73 + *this=other; 1.74 +} 1.75 + 1.76 +PluralRules::~PluralRules() { 1.77 + delete mRules; 1.78 +} 1.79 + 1.80 +PluralRules* 1.81 +PluralRules::clone() const { 1.82 + return new PluralRules(*this); 1.83 +} 1.84 + 1.85 +PluralRules& 1.86 +PluralRules::operator=(const PluralRules& other) { 1.87 + if (this != &other) { 1.88 + delete mRules; 1.89 + if (other.mRules==NULL) { 1.90 + mRules = NULL; 1.91 + } 1.92 + else { 1.93 + mRules = new RuleChain(*other.mRules); 1.94 + } 1.95 + } 1.96 + 1.97 + return *this; 1.98 +} 1.99 + 1.100 +StringEnumeration* PluralRules::getAvailableLocales(UErrorCode &status) { 1.101 + StringEnumeration *result = new PluralAvailableLocalesEnumeration(status); 1.102 + if (result == NULL && U_SUCCESS(status)) { 1.103 + status = U_MEMORY_ALLOCATION_ERROR; 1.104 + } 1.105 + if (U_FAILURE(status)) { 1.106 + delete result; 1.107 + result = NULL; 1.108 + } 1.109 + return result; 1.110 +} 1.111 + 1.112 + 1.113 +PluralRules* U_EXPORT2 1.114 +PluralRules::createRules(const UnicodeString& description, UErrorCode& status) { 1.115 + if (U_FAILURE(status)) { 1.116 + return NULL; 1.117 + } 1.118 + 1.119 + PluralRuleParser parser; 1.120 + PluralRules *newRules = new PluralRules(status); 1.121 + if (U_SUCCESS(status) && newRules == NULL) { 1.122 + status = U_MEMORY_ALLOCATION_ERROR; 1.123 + } 1.124 + parser.parse(description, newRules, status); 1.125 + if (U_FAILURE(status)) { 1.126 + delete newRules; 1.127 + newRules = NULL; 1.128 + } 1.129 + return newRules; 1.130 +} 1.131 + 1.132 + 1.133 +PluralRules* U_EXPORT2 1.134 +PluralRules::createDefaultRules(UErrorCode& status) { 1.135 + return createRules(UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1), status); 1.136 +} 1.137 + 1.138 +PluralRules* U_EXPORT2 1.139 +PluralRules::forLocale(const Locale& locale, UErrorCode& status) { 1.140 + return forLocale(locale, UPLURAL_TYPE_CARDINAL, status); 1.141 +} 1.142 + 1.143 +PluralRules* U_EXPORT2 1.144 +PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) { 1.145 + if (U_FAILURE(status)) { 1.146 + return NULL; 1.147 + } 1.148 + if (type >= UPLURAL_TYPE_COUNT) { 1.149 + status = U_ILLEGAL_ARGUMENT_ERROR; 1.150 + return NULL; 1.151 + } 1.152 + PluralRules *newObj = new PluralRules(status); 1.153 + if (newObj==NULL || U_FAILURE(status)) { 1.154 + delete newObj; 1.155 + return NULL; 1.156 + } 1.157 + UnicodeString locRule = newObj->getRuleFromResource(locale, type, status); 1.158 + // TODO: which errors, if any, should be returned? 1.159 + if (locRule.length() == 0) { 1.160 + // Locales with no specific rules (all numbers have the "other" category 1.161 + // will return a U_MISSING_RESOURCE_ERROR at this point. This is not 1.162 + // an error. 1.163 + locRule = UnicodeString(PLURAL_DEFAULT_RULE); 1.164 + status = U_ZERO_ERROR; 1.165 + } 1.166 + PluralRuleParser parser; 1.167 + parser.parse(locRule, newObj, status); 1.168 + // TODO: should rule parse errors be returned, or 1.169 + // should we silently use default rules? 1.170 + // Original impl used default rules. 1.171 + // Ask the question to ICU Core. 1.172 + 1.173 + return newObj; 1.174 +} 1.175 + 1.176 +UnicodeString 1.177 +PluralRules::select(int32_t number) const { 1.178 + return select(FixedDecimal(number)); 1.179 +} 1.180 + 1.181 +UnicodeString 1.182 +PluralRules::select(double number) const { 1.183 + return select(FixedDecimal(number)); 1.184 +} 1.185 + 1.186 +UnicodeString 1.187 +PluralRules::select(const FixedDecimal &number) const { 1.188 + if (mRules == NULL) { 1.189 + return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1); 1.190 + } 1.191 + else { 1.192 + return mRules->select(number); 1.193 + } 1.194 +} 1.195 + 1.196 +StringEnumeration* 1.197 +PluralRules::getKeywords(UErrorCode& status) const { 1.198 + if (U_FAILURE(status)) return NULL; 1.199 + StringEnumeration* nameEnumerator = new PluralKeywordEnumeration(mRules, status); 1.200 + if (U_FAILURE(status)) { 1.201 + delete nameEnumerator; 1.202 + return NULL; 1.203 + } 1.204 + 1.205 + return nameEnumerator; 1.206 +} 1.207 + 1.208 +double 1.209 +PluralRules::getUniqueKeywordValue(const UnicodeString& /* keyword */) { 1.210 + // Not Implemented. 1.211 + return UPLRULES_NO_UNIQUE_VALUE; 1.212 +} 1.213 + 1.214 +int32_t 1.215 +PluralRules::getAllKeywordValues(const UnicodeString & /* keyword */, double * /* dest */, 1.216 + int32_t /* destCapacity */, UErrorCode& error) { 1.217 + error = U_UNSUPPORTED_ERROR; 1.218 + return 0; 1.219 +} 1.220 + 1.221 + 1.222 +static double scaleForInt(double d) { 1.223 + double scale = 1.0; 1.224 + while (d != floor(d)) { 1.225 + d = d * 10.0; 1.226 + scale = scale * 10.0; 1.227 + } 1.228 + return scale; 1.229 +} 1.230 + 1.231 +static int32_t 1.232 +getSamplesFromString(const UnicodeString &samples, double *dest, 1.233 + int32_t destCapacity, UErrorCode& status) { 1.234 + int32_t sampleCount = 0; 1.235 + int32_t sampleStartIdx = 0; 1.236 + int32_t sampleEndIdx = 0; 1.237 + 1.238 + //std::string ss; // TODO: debugging. 1.239 + // std::cout << "PluralRules::getSamples(), samples = \"" << samples.toUTF8String(ss) << "\"\n"; 1.240 + for (sampleCount = 0; sampleCount < destCapacity && sampleStartIdx < samples.length(); ) { 1.241 + sampleEndIdx = samples.indexOf(COMMA, sampleStartIdx); 1.242 + if (sampleEndIdx == -1) { 1.243 + sampleEndIdx = samples.length(); 1.244 + } 1.245 + const UnicodeString &sampleRange = samples.tempSubStringBetween(sampleStartIdx, sampleEndIdx); 1.246 + // ss.erase(); 1.247 + // std::cout << "PluralRules::getSamples(), samplesRange = \"" << sampleRange.toUTF8String(ss) << "\"\n"; 1.248 + int32_t tildeIndex = sampleRange.indexOf(TILDE); 1.249 + if (tildeIndex < 0) { 1.250 + FixedDecimal fixed(sampleRange, status); 1.251 + double sampleValue = fixed.source; 1.252 + if (fixed.visibleDecimalDigitCount == 0 || sampleValue != floor(sampleValue)) { 1.253 + dest[sampleCount++] = sampleValue; 1.254 + } 1.255 + } else { 1.256 + 1.257 + FixedDecimal fixedLo(sampleRange.tempSubStringBetween(0, tildeIndex), status); 1.258 + FixedDecimal fixedHi(sampleRange.tempSubStringBetween(tildeIndex+1), status); 1.259 + double rangeLo = fixedLo.source; 1.260 + double rangeHi = fixedHi.source; 1.261 + if (U_FAILURE(status)) { 1.262 + break; 1.263 + } 1.264 + if (rangeHi < rangeLo) { 1.265 + status = U_INVALID_FORMAT_ERROR; 1.266 + break; 1.267 + } 1.268 + 1.269 + // For ranges of samples with fraction decimal digits, scale the number up so that we 1.270 + // are adding one in the units place. Avoids roundoffs from repetitive adds of tenths. 1.271 + 1.272 + double scale = scaleForInt(rangeLo); 1.273 + double t = scaleForInt(rangeHi); 1.274 + if (t > scale) { 1.275 + scale = t; 1.276 + } 1.277 + rangeLo *= scale; 1.278 + rangeHi *= scale; 1.279 + for (double n=rangeLo; n<=rangeHi; n+=1) { 1.280 + // Hack Alert: don't return any decimal samples with integer values that 1.281 + // originated from a format with trailing decimals. 1.282 + // This API is returning doubles, which can't distinguish having displayed 1.283 + // zeros to the right of the decimal. 1.284 + // This results in test failures with values mapping back to a different keyword. 1.285 + double sampleValue = n/scale; 1.286 + if (!(sampleValue == floor(sampleValue) && fixedLo.visibleDecimalDigitCount > 0)) { 1.287 + dest[sampleCount++] = sampleValue; 1.288 + } 1.289 + if (sampleCount >= destCapacity) { 1.290 + break; 1.291 + } 1.292 + } 1.293 + } 1.294 + sampleStartIdx = sampleEndIdx + 1; 1.295 + } 1.296 + return sampleCount; 1.297 +} 1.298 + 1.299 + 1.300 +int32_t 1.301 +PluralRules::getSamples(const UnicodeString &keyword, double *dest, 1.302 + int32_t destCapacity, UErrorCode& status) { 1.303 + RuleChain *rc = rulesForKeyword(keyword); 1.304 + if (rc == NULL || destCapacity == 0 || U_FAILURE(status)) { 1.305 + return 0; 1.306 + } 1.307 + int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, dest, destCapacity, status); 1.308 + if (numSamples == 0) { 1.309 + numSamples = getSamplesFromString(rc->fDecimalSamples, dest, destCapacity, status); 1.310 + } 1.311 + return numSamples; 1.312 +} 1.313 + 1.314 + 1.315 +RuleChain *PluralRules::rulesForKeyword(const UnicodeString &keyword) const { 1.316 + RuleChain *rc; 1.317 + for (rc = mRules; rc != NULL; rc = rc->fNext) { 1.318 + if (rc->fKeyword == keyword) { 1.319 + break; 1.320 + } 1.321 + } 1.322 + return rc; 1.323 +} 1.324 + 1.325 + 1.326 +UBool 1.327 +PluralRules::isKeyword(const UnicodeString& keyword) const { 1.328 + if (0 == keyword.compare(PLURAL_KEYWORD_OTHER, 5)) { 1.329 + return true; 1.330 + } 1.331 + return rulesForKeyword(keyword) != NULL; 1.332 +} 1.333 + 1.334 +UnicodeString 1.335 +PluralRules::getKeywordOther() const { 1.336 + return UnicodeString(TRUE, PLURAL_KEYWORD_OTHER, 5); 1.337 +} 1.338 + 1.339 +UBool 1.340 +PluralRules::operator==(const PluralRules& other) const { 1.341 + const UnicodeString *ptrKeyword; 1.342 + UErrorCode status= U_ZERO_ERROR; 1.343 + 1.344 + if ( this == &other ) { 1.345 + return TRUE; 1.346 + } 1.347 + LocalPointer<StringEnumeration> myKeywordList(getKeywords(status)); 1.348 + LocalPointer<StringEnumeration> otherKeywordList(other.getKeywords(status)); 1.349 + if (U_FAILURE(status)) { 1.350 + return FALSE; 1.351 + } 1.352 + 1.353 + if (myKeywordList->count(status)!=otherKeywordList->count(status)) { 1.354 + return FALSE; 1.355 + } 1.356 + myKeywordList->reset(status); 1.357 + while ((ptrKeyword=myKeywordList->snext(status))!=NULL) { 1.358 + if (!other.isKeyword(*ptrKeyword)) { 1.359 + return FALSE; 1.360 + } 1.361 + } 1.362 + otherKeywordList->reset(status); 1.363 + while ((ptrKeyword=otherKeywordList->snext(status))!=NULL) { 1.364 + if (!this->isKeyword(*ptrKeyword)) { 1.365 + return FALSE; 1.366 + } 1.367 + } 1.368 + if (U_FAILURE(status)) { 1.369 + return FALSE; 1.370 + } 1.371 + 1.372 + return TRUE; 1.373 +} 1.374 + 1.375 + 1.376 +void 1.377 +PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErrorCode &status) 1.378 +{ 1.379 + if (U_FAILURE(status)) { 1.380 + return; 1.381 + } 1.382 + U_ASSERT(ruleIndex == 0); // Parsers are good for a single use only! 1.383 + ruleSrc = &ruleData; 1.384 + 1.385 + while (ruleIndex< ruleSrc->length()) { 1.386 + getNextToken(status); 1.387 + if (U_FAILURE(status)) { 1.388 + return; 1.389 + } 1.390 + checkSyntax(status); 1.391 + if (U_FAILURE(status)) { 1.392 + return; 1.393 + } 1.394 + switch (type) { 1.395 + case tAnd: 1.396 + U_ASSERT(curAndConstraint != NULL); 1.397 + curAndConstraint = curAndConstraint->add(); 1.398 + break; 1.399 + case tOr: 1.400 + { 1.401 + U_ASSERT(currentChain != NULL); 1.402 + OrConstraint *orNode=currentChain->ruleHeader; 1.403 + while (orNode->next != NULL) { 1.404 + orNode = orNode->next; 1.405 + } 1.406 + orNode->next= new OrConstraint(); 1.407 + orNode=orNode->next; 1.408 + orNode->next=NULL; 1.409 + curAndConstraint = orNode->add(); 1.410 + } 1.411 + break; 1.412 + case tIs: 1.413 + U_ASSERT(curAndConstraint != NULL); 1.414 + U_ASSERT(curAndConstraint->value == -1); 1.415 + U_ASSERT(curAndConstraint->rangeList == NULL); 1.416 + break; 1.417 + case tNot: 1.418 + U_ASSERT(curAndConstraint != NULL); 1.419 + curAndConstraint->negated=TRUE; 1.420 + break; 1.421 + 1.422 + case tNotEqual: 1.423 + curAndConstraint->negated=TRUE; 1.424 + case tIn: 1.425 + case tWithin: 1.426 + case tEqual: 1.427 + U_ASSERT(curAndConstraint != NULL); 1.428 + curAndConstraint->rangeList = new UVector32(status); 1.429 + curAndConstraint->rangeList->addElement(-1, status); // range Low 1.430 + curAndConstraint->rangeList->addElement(-1, status); // range Hi 1.431 + rangeLowIdx = 0; 1.432 + rangeHiIdx = 1; 1.433 + curAndConstraint->value=PLURAL_RANGE_HIGH; 1.434 + curAndConstraint->integerOnly = (type != tWithin); 1.435 + break; 1.436 + case tNumber: 1.437 + U_ASSERT(curAndConstraint != NULL); 1.438 + if ( (curAndConstraint->op==AndConstraint::MOD)&& 1.439 + (curAndConstraint->opNum == -1 ) ) { 1.440 + curAndConstraint->opNum=getNumberValue(token); 1.441 + } 1.442 + else { 1.443 + if (curAndConstraint->rangeList == NULL) { 1.444 + // this is for an 'is' rule 1.445 + curAndConstraint->value = getNumberValue(token); 1.446 + } else { 1.447 + // this is for an 'in' or 'within' rule 1.448 + if (curAndConstraint->rangeList->elementAti(rangeLowIdx) == -1) { 1.449 + curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeLowIdx); 1.450 + curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx); 1.451 + } 1.452 + else { 1.453 + curAndConstraint->rangeList->setElementAt(getNumberValue(token), rangeHiIdx); 1.454 + if (curAndConstraint->rangeList->elementAti(rangeLowIdx) > 1.455 + curAndConstraint->rangeList->elementAti(rangeHiIdx)) { 1.456 + // Range Lower bound > Range Upper bound. 1.457 + // U_UNEXPECTED_TOKEN seems a little funny, but it is consistently 1.458 + // used for all plural rule parse errors. 1.459 + status = U_UNEXPECTED_TOKEN; 1.460 + break; 1.461 + } 1.462 + } 1.463 + } 1.464 + } 1.465 + break; 1.466 + case tComma: 1.467 + // TODO: rule syntax checking is inadequate, can happen with badly formed rules. 1.468 + // Catch cases like "n mod 10, is 1" here instead. 1.469 + if (curAndConstraint == NULL || curAndConstraint->rangeList == NULL) { 1.470 + status = U_UNEXPECTED_TOKEN; 1.471 + break; 1.472 + } 1.473 + U_ASSERT(curAndConstraint->rangeList->size() >= 2); 1.474 + rangeLowIdx = curAndConstraint->rangeList->size(); 1.475 + curAndConstraint->rangeList->addElement(-1, status); // range Low 1.476 + rangeHiIdx = curAndConstraint->rangeList->size(); 1.477 + curAndConstraint->rangeList->addElement(-1, status); // range Hi 1.478 + break; 1.479 + case tMod: 1.480 + U_ASSERT(curAndConstraint != NULL); 1.481 + curAndConstraint->op=AndConstraint::MOD; 1.482 + break; 1.483 + case tVariableN: 1.484 + case tVariableI: 1.485 + case tVariableF: 1.486 + case tVariableT: 1.487 + case tVariableV: 1.488 + U_ASSERT(curAndConstraint != NULL); 1.489 + curAndConstraint->digitsType = type; 1.490 + break; 1.491 + case tKeyword: 1.492 + { 1.493 + RuleChain *newChain = new RuleChain; 1.494 + if (newChain == NULL) { 1.495 + status = U_MEMORY_ALLOCATION_ERROR; 1.496 + break; 1.497 + } 1.498 + newChain->fKeyword = token; 1.499 + if (prules->mRules == NULL) { 1.500 + prules->mRules = newChain; 1.501 + } else { 1.502 + // The new rule chain goes at the end of the linked list of rule chains, 1.503 + // unless there is an "other" keyword & chain. "other" must remain last. 1.504 + RuleChain *insertAfter = prules->mRules; 1.505 + while (insertAfter->fNext!=NULL && 1.506 + insertAfter->fNext->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5) != 0 ){ 1.507 + insertAfter=insertAfter->fNext; 1.508 + } 1.509 + newChain->fNext = insertAfter->fNext; 1.510 + insertAfter->fNext = newChain; 1.511 + } 1.512 + OrConstraint *orNode = new OrConstraint(); 1.513 + newChain->ruleHeader = orNode; 1.514 + curAndConstraint = orNode->add(); 1.515 + currentChain = newChain; 1.516 + } 1.517 + break; 1.518 + 1.519 + case tInteger: 1.520 + for (;;) { 1.521 + getNextToken(status); 1.522 + if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) { 1.523 + break; 1.524 + } 1.525 + if (type == tEllipsis) { 1.526 + currentChain->fIntegerSamplesUnbounded = TRUE; 1.527 + continue; 1.528 + } 1.529 + currentChain->fIntegerSamples.append(token); 1.530 + } 1.531 + break; 1.532 + 1.533 + case tDecimal: 1.534 + for (;;) { 1.535 + getNextToken(status); 1.536 + if (U_FAILURE(status) || type == tSemiColon || type == tEOF || type == tAt) { 1.537 + break; 1.538 + } 1.539 + if (type == tEllipsis) { 1.540 + currentChain->fDecimalSamplesUnbounded = TRUE; 1.541 + continue; 1.542 + } 1.543 + currentChain->fDecimalSamples.append(token); 1.544 + } 1.545 + break; 1.546 + 1.547 + default: 1.548 + break; 1.549 + } 1.550 + prevType=type; 1.551 + if (U_FAILURE(status)) { 1.552 + break; 1.553 + } 1.554 + } 1.555 +} 1.556 + 1.557 +UnicodeString 1.558 +PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorCode& errCode) { 1.559 + UnicodeString emptyStr; 1.560 + 1.561 + if (U_FAILURE(errCode)) { 1.562 + return emptyStr; 1.563 + } 1.564 + LocalUResourceBundlePointer rb(ures_openDirect(NULL, "plurals", &errCode)); 1.565 + if(U_FAILURE(errCode)) { 1.566 + return emptyStr; 1.567 + } 1.568 + const char *typeKey; 1.569 + switch (type) { 1.570 + case UPLURAL_TYPE_CARDINAL: 1.571 + typeKey = "locales"; 1.572 + break; 1.573 + case UPLURAL_TYPE_ORDINAL: 1.574 + typeKey = "locales_ordinals"; 1.575 + break; 1.576 + default: 1.577 + // Must not occur: The caller should have checked for valid types. 1.578 + errCode = U_ILLEGAL_ARGUMENT_ERROR; 1.579 + return emptyStr; 1.580 + } 1.581 + LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), typeKey, NULL, &errCode)); 1.582 + if(U_FAILURE(errCode)) { 1.583 + return emptyStr; 1.584 + } 1.585 + int32_t resLen=0; 1.586 + const char *curLocaleName=locale.getName(); 1.587 + const UChar* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &errCode); 1.588 + 1.589 + if (s == NULL) { 1.590 + // Check parent locales. 1.591 + UErrorCode status = U_ZERO_ERROR; 1.592 + char parentLocaleName[ULOC_FULLNAME_CAPACITY]; 1.593 + const char *curLocaleName=locale.getName(); 1.594 + uprv_strcpy(parentLocaleName, curLocaleName); 1.595 + 1.596 + while (uloc_getParent(parentLocaleName, parentLocaleName, 1.597 + ULOC_FULLNAME_CAPACITY, &status) > 0) { 1.598 + resLen=0; 1.599 + s = ures_getStringByKey(locRes.getAlias(), parentLocaleName, &resLen, &status); 1.600 + if (s != NULL) { 1.601 + errCode = U_ZERO_ERROR; 1.602 + break; 1.603 + } 1.604 + status = U_ZERO_ERROR; 1.605 + } 1.606 + } 1.607 + if (s==NULL) { 1.608 + return emptyStr; 1.609 + } 1.610 + 1.611 + char setKey[256]; 1.612 + u_UCharsToChars(s, setKey, resLen + 1); 1.613 + // printf("\n PluralRule: %s\n", setKey); 1.614 + 1.615 + LocalUResourceBundlePointer ruleRes(ures_getByKey(rb.getAlias(), "rules", NULL, &errCode)); 1.616 + if(U_FAILURE(errCode)) { 1.617 + return emptyStr; 1.618 + } 1.619 + LocalUResourceBundlePointer setRes(ures_getByKey(ruleRes.getAlias(), setKey, NULL, &errCode)); 1.620 + if (U_FAILURE(errCode)) { 1.621 + return emptyStr; 1.622 + } 1.623 + 1.624 + int32_t numberKeys = ures_getSize(setRes.getAlias()); 1.625 + UnicodeString result; 1.626 + const char *key=NULL; 1.627 + for(int32_t i=0; i<numberKeys; ++i) { // Keys are zero, one, few, ... 1.628 + UnicodeString rules = ures_getNextUnicodeString(setRes.getAlias(), &key, &errCode); 1.629 + UnicodeString uKey(key, -1, US_INV); 1.630 + result.append(uKey); 1.631 + result.append(COLON); 1.632 + result.append(rules); 1.633 + result.append(SEMI_COLON); 1.634 + } 1.635 + return result; 1.636 +} 1.637 + 1.638 + 1.639 +UnicodeString 1.640 +PluralRules::getRules() const { 1.641 + UnicodeString rules; 1.642 + if (mRules != NULL) { 1.643 + mRules->dumpRules(rules); 1.644 + } 1.645 + return rules; 1.646 +} 1.647 + 1.648 + 1.649 +AndConstraint::AndConstraint() { 1.650 + op = AndConstraint::NONE; 1.651 + opNum=-1; 1.652 + value = -1; 1.653 + rangeList = NULL; 1.654 + negated = FALSE; 1.655 + integerOnly = FALSE; 1.656 + digitsType = none; 1.657 + next=NULL; 1.658 +} 1.659 + 1.660 + 1.661 +AndConstraint::AndConstraint(const AndConstraint& other) { 1.662 + this->op = other.op; 1.663 + this->opNum=other.opNum; 1.664 + this->value=other.value; 1.665 + this->rangeList=NULL; 1.666 + if (other.rangeList != NULL) { 1.667 + UErrorCode status = U_ZERO_ERROR; 1.668 + this->rangeList = new UVector32(status); 1.669 + this->rangeList->assign(*other.rangeList, status); 1.670 + } 1.671 + this->integerOnly=other.integerOnly; 1.672 + this->negated=other.negated; 1.673 + this->digitsType = other.digitsType; 1.674 + if (other.next==NULL) { 1.675 + this->next=NULL; 1.676 + } 1.677 + else { 1.678 + this->next = new AndConstraint(*other.next); 1.679 + } 1.680 +} 1.681 + 1.682 +AndConstraint::~AndConstraint() { 1.683 + delete rangeList; 1.684 + if (next!=NULL) { 1.685 + delete next; 1.686 + } 1.687 +} 1.688 + 1.689 + 1.690 +UBool 1.691 +AndConstraint::isFulfilled(const FixedDecimal &number) { 1.692 + UBool result = TRUE; 1.693 + if (digitsType == none) { 1.694 + // An empty AndConstraint, created by a rule with a keyword but no following expression. 1.695 + return TRUE; 1.696 + } 1.697 + double n = number.get(digitsType); // pulls n | i | v | f value for the number. 1.698 + // Will always be positive. 1.699 + // May be non-integer (n option only) 1.700 + do { 1.701 + if (integerOnly && n != uprv_floor(n)) { 1.702 + result = FALSE; 1.703 + break; 1.704 + } 1.705 + 1.706 + if (op == MOD) { 1.707 + n = fmod(n, opNum); 1.708 + } 1.709 + if (rangeList == NULL) { 1.710 + result = value == -1 || // empty rule 1.711 + n == value; // 'is' rule 1.712 + break; 1.713 + } 1.714 + result = FALSE; // 'in' or 'within' rule 1.715 + for (int32_t r=0; r<rangeList->size(); r+=2) { 1.716 + if (rangeList->elementAti(r) <= n && n <= rangeList->elementAti(r+1)) { 1.717 + result = TRUE; 1.718 + break; 1.719 + } 1.720 + } 1.721 + } while (FALSE); 1.722 + 1.723 + if (negated) { 1.724 + result = !result; 1.725 + } 1.726 + return result; 1.727 +} 1.728 + 1.729 + 1.730 +AndConstraint* 1.731 +AndConstraint::add() 1.732 +{ 1.733 + this->next = new AndConstraint(); 1.734 + return this->next; 1.735 +} 1.736 + 1.737 +OrConstraint::OrConstraint() { 1.738 + childNode=NULL; 1.739 + next=NULL; 1.740 +} 1.741 + 1.742 +OrConstraint::OrConstraint(const OrConstraint& other) { 1.743 + if ( other.childNode == NULL ) { 1.744 + this->childNode = NULL; 1.745 + } 1.746 + else { 1.747 + this->childNode = new AndConstraint(*(other.childNode)); 1.748 + } 1.749 + if (other.next == NULL ) { 1.750 + this->next = NULL; 1.751 + } 1.752 + else { 1.753 + this->next = new OrConstraint(*(other.next)); 1.754 + } 1.755 +} 1.756 + 1.757 +OrConstraint::~OrConstraint() { 1.758 + if (childNode!=NULL) { 1.759 + delete childNode; 1.760 + } 1.761 + if (next!=NULL) { 1.762 + delete next; 1.763 + } 1.764 +} 1.765 + 1.766 +AndConstraint* 1.767 +OrConstraint::add() 1.768 +{ 1.769 + OrConstraint *curOrConstraint=this; 1.770 + { 1.771 + while (curOrConstraint->next!=NULL) { 1.772 + curOrConstraint = curOrConstraint->next; 1.773 + } 1.774 + U_ASSERT(curOrConstraint->childNode == NULL); 1.775 + curOrConstraint->childNode = new AndConstraint(); 1.776 + } 1.777 + return curOrConstraint->childNode; 1.778 +} 1.779 + 1.780 +UBool 1.781 +OrConstraint::isFulfilled(const FixedDecimal &number) { 1.782 + OrConstraint* orRule=this; 1.783 + UBool result=FALSE; 1.784 + 1.785 + while (orRule!=NULL && !result) { 1.786 + result=TRUE; 1.787 + AndConstraint* andRule = orRule->childNode; 1.788 + while (andRule!=NULL && result) { 1.789 + result = andRule->isFulfilled(number); 1.790 + andRule=andRule->next; 1.791 + } 1.792 + orRule = orRule->next; 1.793 + } 1.794 + 1.795 + return result; 1.796 +} 1.797 + 1.798 + 1.799 +RuleChain::RuleChain(): fKeyword(), fNext(NULL), ruleHeader(NULL), fDecimalSamples(), fIntegerSamples(), 1.800 + fDecimalSamplesUnbounded(FALSE), fIntegerSamplesUnbounded(FALSE) { 1.801 +} 1.802 + 1.803 +RuleChain::RuleChain(const RuleChain& other) : 1.804 + fKeyword(other.fKeyword), fNext(NULL), ruleHeader(NULL), fDecimalSamples(other.fDecimalSamples), 1.805 + fIntegerSamples(other.fIntegerSamples), fDecimalSamplesUnbounded(other.fDecimalSamplesUnbounded), 1.806 + fIntegerSamplesUnbounded(other.fIntegerSamplesUnbounded) { 1.807 + if (other.ruleHeader != NULL) { 1.808 + this->ruleHeader = new OrConstraint(*(other.ruleHeader)); 1.809 + } 1.810 + if (other.fNext != NULL ) { 1.811 + this->fNext = new RuleChain(*other.fNext); 1.812 + } 1.813 +} 1.814 + 1.815 +RuleChain::~RuleChain() { 1.816 + delete fNext; 1.817 + delete ruleHeader; 1.818 +} 1.819 + 1.820 + 1.821 +UnicodeString 1.822 +RuleChain::select(const FixedDecimal &number) const { 1.823 + if (!number.isNanOrInfinity) { 1.824 + for (const RuleChain *rules = this; rules != NULL; rules = rules->fNext) { 1.825 + if (rules->ruleHeader->isFulfilled(number)) { 1.826 + return rules->fKeyword; 1.827 + } 1.828 + } 1.829 + } 1.830 + return UnicodeString(TRUE, PLURAL_KEYWORD_OTHER, 5); 1.831 +} 1.832 + 1.833 +static UnicodeString tokenString(tokenType tok) { 1.834 + UnicodeString s; 1.835 + switch (tok) { 1.836 + case tVariableN: 1.837 + s.append(LOW_N); break; 1.838 + case tVariableI: 1.839 + s.append(LOW_I); break; 1.840 + case tVariableF: 1.841 + s.append(LOW_F); break; 1.842 + case tVariableV: 1.843 + s.append(LOW_V); break; 1.844 + case tVariableT: 1.845 + s.append(LOW_T); break; 1.846 + default: 1.847 + s.append(TILDE); 1.848 + } 1.849 + return s; 1.850 +} 1.851 + 1.852 +void 1.853 +RuleChain::dumpRules(UnicodeString& result) { 1.854 + UChar digitString[16]; 1.855 + 1.856 + if ( ruleHeader != NULL ) { 1.857 + result += fKeyword; 1.858 + result += COLON; 1.859 + result += SPACE; 1.860 + OrConstraint* orRule=ruleHeader; 1.861 + while ( orRule != NULL ) { 1.862 + AndConstraint* andRule=orRule->childNode; 1.863 + while ( andRule != NULL ) { 1.864 + if ((andRule->op==AndConstraint::NONE) && (andRule->rangeList==NULL) && (andRule->value == -1)) { 1.865 + // Empty Rules. 1.866 + } else if ( (andRule->op==AndConstraint::NONE) && (andRule->rangeList==NULL) ) { 1.867 + result += tokenString(andRule->digitsType); 1.868 + result += UNICODE_STRING_SIMPLE(" is "); 1.869 + if (andRule->negated) { 1.870 + result += UNICODE_STRING_SIMPLE("not "); 1.871 + } 1.872 + uprv_itou(digitString,16, andRule->value,10,0); 1.873 + result += UnicodeString(digitString); 1.874 + } 1.875 + else { 1.876 + result += tokenString(andRule->digitsType); 1.877 + result += SPACE; 1.878 + if (andRule->op==AndConstraint::MOD) { 1.879 + result += UNICODE_STRING_SIMPLE("mod "); 1.880 + uprv_itou(digitString,16, andRule->opNum,10,0); 1.881 + result += UnicodeString(digitString); 1.882 + } 1.883 + if (andRule->rangeList==NULL) { 1.884 + if (andRule->negated) { 1.885 + result += UNICODE_STRING_SIMPLE(" is not "); 1.886 + uprv_itou(digitString,16, andRule->value,10,0); 1.887 + result += UnicodeString(digitString); 1.888 + } 1.889 + else { 1.890 + result += UNICODE_STRING_SIMPLE(" is "); 1.891 + uprv_itou(digitString,16, andRule->value,10,0); 1.892 + result += UnicodeString(digitString); 1.893 + } 1.894 + } 1.895 + else { 1.896 + if (andRule->negated) { 1.897 + if ( andRule->integerOnly ) { 1.898 + result += UNICODE_STRING_SIMPLE(" not in "); 1.899 + } 1.900 + else { 1.901 + result += UNICODE_STRING_SIMPLE(" not within "); 1.902 + } 1.903 + } 1.904 + else { 1.905 + if ( andRule->integerOnly ) { 1.906 + result += UNICODE_STRING_SIMPLE(" in "); 1.907 + } 1.908 + else { 1.909 + result += UNICODE_STRING_SIMPLE(" within "); 1.910 + } 1.911 + } 1.912 + for (int32_t r=0; r<andRule->rangeList->size(); r+=2) { 1.913 + int32_t rangeLo = andRule->rangeList->elementAti(r); 1.914 + int32_t rangeHi = andRule->rangeList->elementAti(r+1); 1.915 + uprv_itou(digitString,16, rangeLo, 10, 0); 1.916 + result += UnicodeString(digitString); 1.917 + result += UNICODE_STRING_SIMPLE(".."); 1.918 + uprv_itou(digitString,16, rangeHi, 10,0); 1.919 + result += UnicodeString(digitString); 1.920 + if (r+2 < andRule->rangeList->size()) { 1.921 + result += UNICODE_STRING_SIMPLE(", "); 1.922 + } 1.923 + } 1.924 + } 1.925 + } 1.926 + if ( (andRule=andRule->next) != NULL) { 1.927 + result += UNICODE_STRING_SIMPLE(" and "); 1.928 + } 1.929 + } 1.930 + if ( (orRule = orRule->next) != NULL ) { 1.931 + result += UNICODE_STRING_SIMPLE(" or "); 1.932 + } 1.933 + } 1.934 + } 1.935 + if ( fNext != NULL ) { 1.936 + result += UNICODE_STRING_SIMPLE("; "); 1.937 + fNext->dumpRules(result); 1.938 + } 1.939 +} 1.940 + 1.941 + 1.942 +UErrorCode 1.943 +RuleChain::getKeywords(int32_t capacityOfKeywords, UnicodeString* keywords, int32_t& arraySize) const { 1.944 + if ( arraySize < capacityOfKeywords-1 ) { 1.945 + keywords[arraySize++]=fKeyword; 1.946 + } 1.947 + else { 1.948 + return U_BUFFER_OVERFLOW_ERROR; 1.949 + } 1.950 + 1.951 + if ( fNext != NULL ) { 1.952 + return fNext->getKeywords(capacityOfKeywords, keywords, arraySize); 1.953 + } 1.954 + else { 1.955 + return U_ZERO_ERROR; 1.956 + } 1.957 +} 1.958 + 1.959 +UBool 1.960 +RuleChain::isKeyword(const UnicodeString& keywordParam) const { 1.961 + if ( fKeyword == keywordParam ) { 1.962 + return TRUE; 1.963 + } 1.964 + 1.965 + if ( fNext != NULL ) { 1.966 + return fNext->isKeyword(keywordParam); 1.967 + } 1.968 + else { 1.969 + return FALSE; 1.970 + } 1.971 +} 1.972 + 1.973 + 1.974 +PluralRuleParser::PluralRuleParser() : 1.975 + ruleIndex(0), token(), type(none), prevType(none), 1.976 + curAndConstraint(NULL), currentChain(NULL), rangeLowIdx(-1), rangeHiIdx(-1) 1.977 +{ 1.978 +} 1.979 + 1.980 +PluralRuleParser::~PluralRuleParser() { 1.981 +} 1.982 + 1.983 + 1.984 +int32_t 1.985 +PluralRuleParser::getNumberValue(const UnicodeString& token) { 1.986 + int32_t i; 1.987 + char digits[128]; 1.988 + 1.989 + i = token.extract(0, token.length(), digits, ARRAY_SIZE(digits), US_INV); 1.990 + digits[i]='\0'; 1.991 + 1.992 + return((int32_t)atoi(digits)); 1.993 +} 1.994 + 1.995 + 1.996 +void 1.997 +PluralRuleParser::checkSyntax(UErrorCode &status) 1.998 +{ 1.999 + if (U_FAILURE(status)) { 1.1000 + return; 1.1001 + } 1.1002 + if (!(prevType==none || prevType==tSemiColon)) { 1.1003 + type = getKeyType(token, type); // Switch token type from tKeyword if we scanned a reserved word, 1.1004 + // and we are not at the start of a rule, where a 1.1005 + // keyword is expected. 1.1006 + } 1.1007 + 1.1008 + switch(prevType) { 1.1009 + case none: 1.1010 + case tSemiColon: 1.1011 + if (type!=tKeyword && type != tEOF) { 1.1012 + status = U_UNEXPECTED_TOKEN; 1.1013 + } 1.1014 + break; 1.1015 + case tVariableN: 1.1016 + case tVariableI: 1.1017 + case tVariableF: 1.1018 + case tVariableT: 1.1019 + case tVariableV: 1.1020 + if (type != tIs && type != tMod && type != tIn && 1.1021 + type != tNot && type != tWithin && type != tEqual && type != tNotEqual) { 1.1022 + status = U_UNEXPECTED_TOKEN; 1.1023 + } 1.1024 + break; 1.1025 + case tKeyword: 1.1026 + if (type != tColon) { 1.1027 + status = U_UNEXPECTED_TOKEN; 1.1028 + } 1.1029 + break; 1.1030 + case tColon: 1.1031 + if (!(type == tVariableN || 1.1032 + type == tVariableI || 1.1033 + type == tVariableF || 1.1034 + type == tVariableT || 1.1035 + type == tVariableV || 1.1036 + type == tAt)) { 1.1037 + status = U_UNEXPECTED_TOKEN; 1.1038 + } 1.1039 + break; 1.1040 + case tIs: 1.1041 + if ( type != tNumber && type != tNot) { 1.1042 + status = U_UNEXPECTED_TOKEN; 1.1043 + } 1.1044 + break; 1.1045 + case tNot: 1.1046 + if (type != tNumber && type != tIn && type != tWithin) { 1.1047 + status = U_UNEXPECTED_TOKEN; 1.1048 + } 1.1049 + break; 1.1050 + case tMod: 1.1051 + case tDot2: 1.1052 + case tIn: 1.1053 + case tWithin: 1.1054 + case tEqual: 1.1055 + case tNotEqual: 1.1056 + if (type != tNumber) { 1.1057 + status = U_UNEXPECTED_TOKEN; 1.1058 + } 1.1059 + break; 1.1060 + case tAnd: 1.1061 + case tOr: 1.1062 + if ( type != tVariableN && 1.1063 + type != tVariableI && 1.1064 + type != tVariableF && 1.1065 + type != tVariableT && 1.1066 + type != tVariableV) { 1.1067 + status = U_UNEXPECTED_TOKEN; 1.1068 + } 1.1069 + break; 1.1070 + case tComma: 1.1071 + if (type != tNumber) { 1.1072 + status = U_UNEXPECTED_TOKEN; 1.1073 + } 1.1074 + break; 1.1075 + case tNumber: 1.1076 + if (type != tDot2 && type != tSemiColon && type != tIs && type != tNot && 1.1077 + type != tIn && type != tEqual && type != tNotEqual && type != tWithin && 1.1078 + type != tAnd && type != tOr && type != tComma && type != tAt && 1.1079 + type != tEOF) 1.1080 + { 1.1081 + status = U_UNEXPECTED_TOKEN; 1.1082 + } 1.1083 + // TODO: a comma following a number that is not part of a range will be allowed. 1.1084 + // It's not the only case of this sort of thing. Parser needs a re-write. 1.1085 + break; 1.1086 + case tAt: 1.1087 + if (type != tDecimal && type != tInteger) { 1.1088 + status = U_UNEXPECTED_TOKEN; 1.1089 + } 1.1090 + break; 1.1091 + default: 1.1092 + status = U_UNEXPECTED_TOKEN; 1.1093 + break; 1.1094 + } 1.1095 +} 1.1096 + 1.1097 + 1.1098 +/* 1.1099 + * Scan the next token from the input rules. 1.1100 + * rules and returned token type are in the parser state variables. 1.1101 + */ 1.1102 +void 1.1103 +PluralRuleParser::getNextToken(UErrorCode &status) 1.1104 +{ 1.1105 + if (U_FAILURE(status)) { 1.1106 + return; 1.1107 + } 1.1108 + 1.1109 + UChar ch; 1.1110 + while (ruleIndex < ruleSrc->length()) { 1.1111 + ch = ruleSrc->charAt(ruleIndex); 1.1112 + type = charType(ch); 1.1113 + if (type != tSpace) { 1.1114 + break; 1.1115 + } 1.1116 + ++(ruleIndex); 1.1117 + } 1.1118 + if (ruleIndex >= ruleSrc->length()) { 1.1119 + type = tEOF; 1.1120 + return; 1.1121 + } 1.1122 + int32_t curIndex= ruleIndex; 1.1123 + 1.1124 + switch (type) { 1.1125 + case tColon: 1.1126 + case tSemiColon: 1.1127 + case tComma: 1.1128 + case tEllipsis: 1.1129 + case tTilde: // scanned '~' 1.1130 + case tAt: // scanned '@' 1.1131 + case tEqual: // scanned '=' 1.1132 + case tMod: // scanned '%' 1.1133 + // Single character tokens. 1.1134 + ++curIndex; 1.1135 + break; 1.1136 + 1.1137 + case tNotEqual: // scanned '!' 1.1138 + if (ruleSrc->charAt(curIndex+1) == EQUALS) { 1.1139 + curIndex += 2; 1.1140 + } else { 1.1141 + type = none; 1.1142 + curIndex += 1; 1.1143 + } 1.1144 + break; 1.1145 + 1.1146 + case tKeyword: 1.1147 + while (type == tKeyword && ++curIndex < ruleSrc->length()) { 1.1148 + ch = ruleSrc->charAt(curIndex); 1.1149 + type = charType(ch); 1.1150 + } 1.1151 + type = tKeyword; 1.1152 + break; 1.1153 + 1.1154 + case tNumber: 1.1155 + while (type == tNumber && ++curIndex < ruleSrc->length()) { 1.1156 + ch = ruleSrc->charAt(curIndex); 1.1157 + type = charType(ch); 1.1158 + } 1.1159 + type = tNumber; 1.1160 + break; 1.1161 + 1.1162 + case tDot: 1.1163 + // We could be looking at either ".." in a range, or "..." at the end of a sample. 1.1164 + if (curIndex+1 >= ruleSrc->length() || ruleSrc->charAt(curIndex+1) != DOT) { 1.1165 + ++curIndex; 1.1166 + break; // Single dot 1.1167 + } 1.1168 + if (curIndex+2 >= ruleSrc->length() || ruleSrc->charAt(curIndex+2) != DOT) { 1.1169 + curIndex += 2; 1.1170 + type = tDot2; 1.1171 + break; // double dot 1.1172 + } 1.1173 + type = tEllipsis; 1.1174 + curIndex += 3; 1.1175 + break; // triple dot 1.1176 + 1.1177 + default: 1.1178 + status = U_UNEXPECTED_TOKEN; 1.1179 + ++curIndex; 1.1180 + break; 1.1181 + } 1.1182 + 1.1183 + U_ASSERT(ruleIndex <= ruleSrc->length()); 1.1184 + U_ASSERT(curIndex <= ruleSrc->length()); 1.1185 + token=UnicodeString(*ruleSrc, ruleIndex, curIndex-ruleIndex); 1.1186 + ruleIndex = curIndex; 1.1187 +} 1.1188 + 1.1189 +tokenType 1.1190 +PluralRuleParser::charType(UChar ch) { 1.1191 + if ((ch>=U_ZERO) && (ch<=U_NINE)) { 1.1192 + return tNumber; 1.1193 + } 1.1194 + if (ch>=LOW_A && ch<=LOW_Z) { 1.1195 + return tKeyword; 1.1196 + } 1.1197 + switch (ch) { 1.1198 + case COLON: 1.1199 + return tColon; 1.1200 + case SPACE: 1.1201 + return tSpace; 1.1202 + case SEMI_COLON: 1.1203 + return tSemiColon; 1.1204 + case DOT: 1.1205 + return tDot; 1.1206 + case COMMA: 1.1207 + return tComma; 1.1208 + case EXCLAMATION: 1.1209 + return tNotEqual; 1.1210 + case EQUALS: 1.1211 + return tEqual; 1.1212 + case PERCENT_SIGN: 1.1213 + return tMod; 1.1214 + case AT: 1.1215 + return tAt; 1.1216 + case ELLIPSIS: 1.1217 + return tEllipsis; 1.1218 + case TILDE: 1.1219 + return tTilde; 1.1220 + default : 1.1221 + return none; 1.1222 + } 1.1223 +} 1.1224 + 1.1225 + 1.1226 +// Set token type for reserved words in the Plural Rule syntax. 1.1227 + 1.1228 +tokenType 1.1229 +PluralRuleParser::getKeyType(const UnicodeString &token, tokenType keyType) 1.1230 +{ 1.1231 + if (keyType != tKeyword) { 1.1232 + return keyType; 1.1233 + } 1.1234 + 1.1235 + if (0 == token.compare(PK_VAR_N, 1)) { 1.1236 + keyType = tVariableN; 1.1237 + } else if (0 == token.compare(PK_VAR_I, 1)) { 1.1238 + keyType = tVariableI; 1.1239 + } else if (0 == token.compare(PK_VAR_F, 1)) { 1.1240 + keyType = tVariableF; 1.1241 + } else if (0 == token.compare(PK_VAR_T, 1)) { 1.1242 + keyType = tVariableT; 1.1243 + } else if (0 == token.compare(PK_VAR_V, 1)) { 1.1244 + keyType = tVariableV; 1.1245 + } else if (0 == token.compare(PK_IS, 2)) { 1.1246 + keyType = tIs; 1.1247 + } else if (0 == token.compare(PK_AND, 3)) { 1.1248 + keyType = tAnd; 1.1249 + } else if (0 == token.compare(PK_IN, 2)) { 1.1250 + keyType = tIn; 1.1251 + } else if (0 == token.compare(PK_WITHIN, 6)) { 1.1252 + keyType = tWithin; 1.1253 + } else if (0 == token.compare(PK_NOT, 3)) { 1.1254 + keyType = tNot; 1.1255 + } else if (0 == token.compare(PK_MOD, 3)) { 1.1256 + keyType = tMod; 1.1257 + } else if (0 == token.compare(PK_OR, 2)) { 1.1258 + keyType = tOr; 1.1259 + } else if (0 == token.compare(PK_DECIMAL, 7)) { 1.1260 + keyType = tDecimal; 1.1261 + } else if (0 == token.compare(PK_INTEGER, 7)) { 1.1262 + keyType = tInteger; 1.1263 + } 1.1264 + return keyType; 1.1265 +} 1.1266 + 1.1267 + 1.1268 +PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode& status) 1.1269 + : pos(0), fKeywordNames(status) { 1.1270 + if (U_FAILURE(status)) { 1.1271 + return; 1.1272 + } 1.1273 + fKeywordNames.setDeleter(uprv_deleteUObject); 1.1274 + UBool addKeywordOther=TRUE; 1.1275 + RuleChain *node=header; 1.1276 + while(node!=NULL) { 1.1277 + fKeywordNames.addElement(new UnicodeString(node->fKeyword), status); 1.1278 + if (U_FAILURE(status)) { 1.1279 + return; 1.1280 + } 1.1281 + if (0 == node->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5)) { 1.1282 + addKeywordOther= FALSE; 1.1283 + } 1.1284 + node=node->fNext; 1.1285 + } 1.1286 + 1.1287 + if (addKeywordOther) { 1.1288 + fKeywordNames.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER), status); 1.1289 + } 1.1290 +} 1.1291 + 1.1292 +const UnicodeString* 1.1293 +PluralKeywordEnumeration::snext(UErrorCode& status) { 1.1294 + if (U_SUCCESS(status) && pos < fKeywordNames.size()) { 1.1295 + return (const UnicodeString*)fKeywordNames.elementAt(pos++); 1.1296 + } 1.1297 + return NULL; 1.1298 +} 1.1299 + 1.1300 +void 1.1301 +PluralKeywordEnumeration::reset(UErrorCode& /*status*/) { 1.1302 + pos=0; 1.1303 +} 1.1304 + 1.1305 +int32_t 1.1306 +PluralKeywordEnumeration::count(UErrorCode& /*status*/) const { 1.1307 + return fKeywordNames.size(); 1.1308 +} 1.1309 + 1.1310 +PluralKeywordEnumeration::~PluralKeywordEnumeration() { 1.1311 +} 1.1312 + 1.1313 + 1.1314 + 1.1315 +FixedDecimal::FixedDecimal(double n, int32_t v, int64_t f) { 1.1316 + init(n, v, f); 1.1317 + // check values. TODO make into unit test. 1.1318 + // 1.1319 + // long visiblePower = (int) Math.pow(10, v); 1.1320 + // if (decimalDigits > visiblePower) { 1.1321 + // throw new IllegalArgumentException(); 1.1322 + // } 1.1323 + // double fraction = intValue + (decimalDigits / (double) visiblePower); 1.1324 + // if (fraction != source) { 1.1325 + // double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source)); 1.1326 + // if (diff > 0.00000001d) { 1.1327 + // throw new IllegalArgumentException(); 1.1328 + // } 1.1329 + // } 1.1330 +} 1.1331 + 1.1332 +FixedDecimal::FixedDecimal(double n, int32_t v) { 1.1333 + // Ugly, but for samples we don't care. 1.1334 + init(n, v, getFractionalDigits(n, v)); 1.1335 +} 1.1336 + 1.1337 +FixedDecimal::FixedDecimal(double n) { 1.1338 + init(n); 1.1339 +} 1.1340 + 1.1341 +FixedDecimal::FixedDecimal() { 1.1342 + init(0, 0, 0); 1.1343 +} 1.1344 + 1.1345 + 1.1346 +// Create a FixedDecimal from a UnicodeString containing a number. 1.1347 +// Inefficient, but only used for samples, so simplicity trumps efficiency. 1.1348 + 1.1349 +FixedDecimal::FixedDecimal(const UnicodeString &num, UErrorCode &status) { 1.1350 + CharString cs; 1.1351 + cs.appendInvariantChars(num, status); 1.1352 + DigitList dl; 1.1353 + dl.set(cs.toStringPiece(), status); 1.1354 + if (U_FAILURE(status)) { 1.1355 + init(0, 0, 0); 1.1356 + return; 1.1357 + } 1.1358 + int32_t decimalPoint = num.indexOf(DOT); 1.1359 + double n = dl.getDouble(); 1.1360 + if (decimalPoint == -1) { 1.1361 + init(n, 0, 0); 1.1362 + } else { 1.1363 + int32_t v = num.length() - decimalPoint - 1; 1.1364 + init(n, v, getFractionalDigits(n, v)); 1.1365 + } 1.1366 +} 1.1367 + 1.1368 + 1.1369 +FixedDecimal::FixedDecimal(const FixedDecimal &other) { 1.1370 + source = other.source; 1.1371 + visibleDecimalDigitCount = other.visibleDecimalDigitCount; 1.1372 + decimalDigits = other.decimalDigits; 1.1373 + decimalDigitsWithoutTrailingZeros = other.decimalDigitsWithoutTrailingZeros; 1.1374 + intValue = other.intValue; 1.1375 + hasIntegerValue = other.hasIntegerValue; 1.1376 + isNegative = other.isNegative; 1.1377 + isNanOrInfinity = other.isNanOrInfinity; 1.1378 +} 1.1379 + 1.1380 + 1.1381 +void FixedDecimal::init(double n) { 1.1382 + int32_t numFractionDigits = decimals(n); 1.1383 + init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits)); 1.1384 +} 1.1385 + 1.1386 + 1.1387 +void FixedDecimal::init(double n, int32_t v, int64_t f) { 1.1388 + isNegative = n < 0.0; 1.1389 + source = fabs(n); 1.1390 + isNanOrInfinity = uprv_isNaN(source) || uprv_isPositiveInfinity(source); 1.1391 + if (isNanOrInfinity) { 1.1392 + v = 0; 1.1393 + f = 0; 1.1394 + intValue = 0; 1.1395 + hasIntegerValue = FALSE; 1.1396 + } else { 1.1397 + intValue = (int64_t)source; 1.1398 + hasIntegerValue = (source == intValue); 1.1399 + } 1.1400 + 1.1401 + visibleDecimalDigitCount = v; 1.1402 + decimalDigits = f; 1.1403 + if (f == 0) { 1.1404 + decimalDigitsWithoutTrailingZeros = 0; 1.1405 + } else { 1.1406 + int64_t fdwtz = f; 1.1407 + while ((fdwtz%10) == 0) { 1.1408 + fdwtz /= 10; 1.1409 + } 1.1410 + decimalDigitsWithoutTrailingZeros = fdwtz; 1.1411 + } 1.1412 +} 1.1413 + 1.1414 + 1.1415 +// Fast path only exact initialization. Return true if successful. 1.1416 +// Note: Do not multiply by 10 each time through loop, rounding cruft can build 1.1417 +// up that makes the check for an integer result fail. 1.1418 +// A single multiply of the original number works more reliably. 1.1419 +static int32_t p10[] = {1, 10, 100, 1000, 10000}; 1.1420 +UBool FixedDecimal::quickInit(double n) { 1.1421 + UBool success = FALSE; 1.1422 + n = fabs(n); 1.1423 + int32_t numFractionDigits; 1.1424 + for (numFractionDigits = 0; numFractionDigits <= 3; numFractionDigits++) { 1.1425 + double scaledN = n * p10[numFractionDigits]; 1.1426 + if (scaledN == floor(scaledN)) { 1.1427 + success = TRUE; 1.1428 + break; 1.1429 + } 1.1430 + } 1.1431 + if (success) { 1.1432 + init(n, numFractionDigits, getFractionalDigits(n, numFractionDigits)); 1.1433 + } 1.1434 + return success; 1.1435 +} 1.1436 + 1.1437 + 1.1438 + 1.1439 +int32_t FixedDecimal::decimals(double n) { 1.1440 + // Count the number of decimal digits in the fraction part of the number, excluding trailing zeros. 1.1441 + // fastpath the common cases, integers or fractions with 3 or fewer digits 1.1442 + n = fabs(n); 1.1443 + for (int ndigits=0; ndigits<=3; ndigits++) { 1.1444 + double scaledN = n * p10[ndigits]; 1.1445 + if (scaledN == floor(scaledN)) { 1.1446 + return ndigits; 1.1447 + } 1.1448 + } 1.1449 + 1.1450 + // Slow path, convert with sprintf, parse converted output. 1.1451 + char buf[30] = {0}; 1.1452 + sprintf(buf, "%1.15e", n); 1.1453 + // formatted number looks like this: 1.234567890123457e-01 1.1454 + int exponent = atoi(buf+18); 1.1455 + int numFractionDigits = 15; 1.1456 + for (int i=16; ; --i) { 1.1457 + if (buf[i] != '0') { 1.1458 + break; 1.1459 + } 1.1460 + --numFractionDigits; 1.1461 + } 1.1462 + numFractionDigits -= exponent; // Fraction part of fixed point representation. 1.1463 + return numFractionDigits; 1.1464 +} 1.1465 + 1.1466 + 1.1467 +// Get the fraction digits of a double, represented as an integer. 1.1468 +// v is the number of visible fraction digits in the displayed form of the number. 1.1469 +// Example: n = 1001.234, v = 6, result = 234000 1.1470 +// TODO: need to think through how this is used in the plural rule context. 1.1471 +// This function can easily encounter integer overflow, 1.1472 +// and can easily return noise digits when the precision of a double is exceeded. 1.1473 + 1.1474 +int64_t FixedDecimal::getFractionalDigits(double n, int32_t v) { 1.1475 + if (v == 0 || n == floor(n) || uprv_isNaN(n) || uprv_isPositiveInfinity(n)) { 1.1476 + return 0; 1.1477 + } 1.1478 + n = fabs(n); 1.1479 + double fract = n - floor(n); 1.1480 + switch (v) { 1.1481 + case 1: return (int64_t)(fract*10.0 + 0.5); 1.1482 + case 2: return (int64_t)(fract*100.0 + 0.5); 1.1483 + case 3: return (int64_t)(fract*1000.0 + 0.5); 1.1484 + default: 1.1485 + double scaled = floor(fract * pow(10.0, (double)v) + 0.5); 1.1486 + if (scaled > U_INT64_MAX) { 1.1487 + return U_INT64_MAX; 1.1488 + } else { 1.1489 + return (int64_t)scaled; 1.1490 + } 1.1491 + } 1.1492 +} 1.1493 + 1.1494 + 1.1495 +void FixedDecimal::adjustForMinFractionDigits(int32_t minFractionDigits) { 1.1496 + int32_t numTrailingFractionZeros = minFractionDigits - visibleDecimalDigitCount; 1.1497 + if (numTrailingFractionZeros > 0) { 1.1498 + for (int32_t i=0; i<numTrailingFractionZeros; i++) { 1.1499 + // Do not let the decimalDigits value overflow if there are many trailing zeros. 1.1500 + // Limit the value to 18 digits, the most that a 64 bit int can fully represent. 1.1501 + if (decimalDigits >= 100000000000000000LL) { 1.1502 + break; 1.1503 + } 1.1504 + decimalDigits *= 10; 1.1505 + } 1.1506 + visibleDecimalDigitCount += numTrailingFractionZeros; 1.1507 + } 1.1508 +} 1.1509 + 1.1510 + 1.1511 +double FixedDecimal::get(tokenType operand) const { 1.1512 + switch(operand) { 1.1513 + case tVariableN: return source; 1.1514 + case tVariableI: return (double)intValue; 1.1515 + case tVariableF: return (double)decimalDigits; 1.1516 + case tVariableT: return (double)decimalDigitsWithoutTrailingZeros; 1.1517 + case tVariableV: return visibleDecimalDigitCount; 1.1518 + default: 1.1519 + U_ASSERT(FALSE); // unexpected. 1.1520 + return source; 1.1521 + } 1.1522 +} 1.1523 + 1.1524 +int32_t FixedDecimal::getVisibleFractionDigitCount() const { 1.1525 + return visibleDecimalDigitCount; 1.1526 +} 1.1527 + 1.1528 + 1.1529 + 1.1530 +PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) { 1.1531 + fLocales = NULL; 1.1532 + fRes = NULL; 1.1533 + fOpenStatus = status; 1.1534 + if (U_FAILURE(status)) { 1.1535 + return; 1.1536 + } 1.1537 + fOpenStatus = U_ZERO_ERROR; 1.1538 + LocalUResourceBundlePointer rb(ures_openDirect(NULL, "plurals", &fOpenStatus)); 1.1539 + fLocales = ures_getByKey(rb.getAlias(), "locales", NULL, &fOpenStatus); 1.1540 +} 1.1541 + 1.1542 +PluralAvailableLocalesEnumeration::~PluralAvailableLocalesEnumeration() { 1.1543 + ures_close(fLocales); 1.1544 + ures_close(fRes); 1.1545 + fLocales = NULL; 1.1546 + fRes = NULL; 1.1547 +} 1.1548 + 1.1549 +const char *PluralAvailableLocalesEnumeration::next(int32_t *resultLength, UErrorCode &status) { 1.1550 + if (U_FAILURE(status)) { 1.1551 + return NULL; 1.1552 + } 1.1553 + if (U_FAILURE(fOpenStatus)) { 1.1554 + status = fOpenStatus; 1.1555 + return NULL; 1.1556 + } 1.1557 + fRes = ures_getNextResource(fLocales, fRes, &status); 1.1558 + if (fRes == NULL || U_FAILURE(status)) { 1.1559 + if (status == U_INDEX_OUTOFBOUNDS_ERROR) { 1.1560 + status = U_ZERO_ERROR; 1.1561 + } 1.1562 + return NULL; 1.1563 + } 1.1564 + const char *result = ures_getKey(fRes); 1.1565 + if (resultLength != NULL) { 1.1566 + *resultLength = uprv_strlen(result); 1.1567 + } 1.1568 + return result; 1.1569 +} 1.1570 + 1.1571 + 1.1572 +void PluralAvailableLocalesEnumeration::reset(UErrorCode &status) { 1.1573 + if (U_FAILURE(status)) { 1.1574 + return; 1.1575 + } 1.1576 + if (U_FAILURE(fOpenStatus)) { 1.1577 + status = fOpenStatus; 1.1578 + return; 1.1579 + } 1.1580 + ures_resetIterator(fLocales); 1.1581 +} 1.1582 + 1.1583 +int32_t PluralAvailableLocalesEnumeration::count(UErrorCode &status) const { 1.1584 + if (U_FAILURE(status)) { 1.1585 + return 0; 1.1586 + } 1.1587 + if (U_FAILURE(fOpenStatus)) { 1.1588 + status = fOpenStatus; 1.1589 + return 0; 1.1590 + } 1.1591 + return ures_getSize(fLocales); 1.1592 +} 1.1593 + 1.1594 +U_NAMESPACE_END 1.1595 + 1.1596 + 1.1597 +#endif /* #if !UCONFIG_NO_FORMATTING */ 1.1598 + 1.1599 +//eof