StandardPlural::Form QuantityFormatter::selectPlural( const Formattable &number, const NumberFormat &fmt, const PluralRules &rules, UnicodeString &formattedNumber, FieldPosition &pos, UErrorCode &status) { if (U_FAILURE(status)) { return StandardPlural::OTHER; } UnicodeString pluralKeyword; const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt); if (decFmt != NULL) { number::impl::DecimalQuantity dq; decFmt->formatToDecimalQuantity(number, dq, status); if (U_FAILURE(status)) { return StandardPlural::OTHER; } pluralKeyword = rules.select(dq); decFmt->format(number, formattedNumber, pos, status); } else { if (number.getType() == Formattable::kDouble) { pluralKeyword = rules.select(number.getDouble()); } else if (number.getType() == Formattable::kLong) { pluralKeyword = rules.select(number.getLong()); } else if (number.getType() == Formattable::kInt64) { pluralKeyword = rules.select((double) number.getInt64()); } else { status = U_ILLEGAL_ARGUMENT_ERROR; return StandardPlural::OTHER; } fmt.format(number, formattedNumber, pos, status); } return StandardPlural::orOtherFromString(pluralKeyword); }
void PluralRulesTest::testGetSamples() { // TODO: fix samples, re-enable this test. // no get functional equivalent API in ICU4C, so just // test every locale... UErrorCode status = U_ZERO_ERROR; int32_t numLocales; const Locale* locales = Locale::getAvailableLocales(numLocales); double values[1000]; for (int32_t i = 0; U_SUCCESS(status) && i < numLocales; ++i) { PluralRules *rules = PluralRules::forLocale(locales[i], status); if (U_FAILURE(status)) { break; } StringEnumeration *keywords = rules->getKeywords(status); if (U_FAILURE(status)) { delete rules; break; } const UnicodeString* keyword; while (NULL != (keyword = keywords->snext(status))) { int32_t count = rules->getSamples(*keyword, values, LENGTHOF(values), status); if (U_FAILURE(status)) { errln(UNICODE_STRING_SIMPLE("getSamples() failed for locale ") + locales[i].getName() + UNICODE_STRING_SIMPLE(", keyword ") + *keyword); continue; } if (count == 0) { // TODO: Lots of these. // errln(UNICODE_STRING_SIMPLE("no samples for keyword ") + *keyword + UNICODE_STRING_SIMPLE(" in locale ") + locales[i].getName() ); } if (count > LENGTHOF(values)) { errln(UNICODE_STRING_SIMPLE("getSamples()=") + count + UNICODE_STRING_SIMPLE(", too many values, for locale ") + locales[i].getName() + UNICODE_STRING_SIMPLE(", keyword ") + *keyword); count = LENGTHOF(values); } for (int32_t j = 0; j < count; ++j) { if (values[j] == UPLRULES_NO_UNIQUE_VALUE) { errln("got 'no unique value' among values"); } else { UnicodeString resultKeyword = rules->select(values[j]); // if (strcmp(locales[i].getName(), "uk") == 0) { // Debug only. // std::cout << " uk " << US(resultKeyword).cstr() << " " << values[j] << std::endl; // } if (*keyword != resultKeyword) { errln("file %s, line %d, Locale %s, sample for keyword \"%s\": %g, select(%g) returns keyword \"%s\"", __FILE__, __LINE__, locales[i].getName(), US(*keyword).cstr(), values[j], values[j], US(resultKeyword).cstr()); } } } } delete keywords; delete rules; } }
void PluralRulesTest::assertRuleKeyValue(const UnicodeString& rule, const UnicodeString& key, double expected) { UErrorCode status = U_ZERO_ERROR; PluralRules *pr = PluralRules::createRules(rule, status); double result = pr->getUniqueKeywordValue(key); delete pr; if (expected != result) { errln("expected %g but got %g", expected, result); } }
UBool PluralRules::operator==(const PluralRules& other) const { int32_t limit; 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))!=NULL) { if (!other.isKeyword(*ptrKeyword)) { return FALSE; } } otherKeywordList->reset(status); while ((ptrKeyword=otherKeywordList->snext(status))!=NULL) { if (!this->isKeyword(*ptrKeyword)) { return FALSE; } } if (U_FAILURE(status)) { return FALSE; } if ((limit=this->getRepeatLimit()) != other.getRepeatLimit()) { return FALSE; } UnicodeString myKeyword, otherKeyword; for (int32_t i=0; i<limit; ++i) { myKeyword = this->select(i); otherKeyword = other.select(i); if (myKeyword!=otherKeyword) { return FALSE; } } return TRUE; }
UBool checkEqual(const PluralRules &test, char *result, int32_t max) { UnicodeString key; UBool isEqual = TRUE; for (int32_t i=0; i<max; ++i) { key= test.select(i); if ( key.charAt(0)!=result[i] ) { isEqual = FALSE; } } return isEqual; }
PluralRules* U_EXPORT2 PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) { RuleChain rChain; if (U_FAILURE(status)) { return NULL; } if (type >= UPLURAL_TYPE_COUNT) { status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } PluralRules *newObj = new PluralRules(status); if (newObj==NULL || U_FAILURE(status)) { delete newObj; return NULL; } UnicodeString locRule = newObj->getRuleFromResource(locale, type, status); if ((locRule.length() != 0) && U_SUCCESS(status)) { newObj->parseDescription(locRule, rChain, status); if (U_SUCCESS(status)) { newObj->addRules(rChain); } } if (U_FAILURE(status)||(locRule.length() == 0)) { // use default plural rule status = U_ZERO_ERROR; UnicodeString defRule = UnicodeString(PLURAL_DEFAULT_RULE); newObj->parseDescription(defRule, rChain, status); newObj->addRules(rChain); } return newObj; }
PluralRules* U_EXPORT2 PluralRules::createRules(const UnicodeString& description, UErrorCode& status) { RuleChain rules; if (U_FAILURE(status)) { return NULL; } PluralRules *newRules = new PluralRules(status); if ( (newRules != NULL)&& U_SUCCESS(status) ) { newRules->parseDescription((UnicodeString &)description, rules, status); if (U_SUCCESS(status)) { newRules->addRules(rules); } } if (U_FAILURE(status)) { delete newRules; return NULL; } else { return newRules; } }
void PluralRulesTest::testWithin() { // goes to show you what lack of testing will do. // of course, this has been broken for two years and no one has noticed... UErrorCode status = U_ZERO_ERROR; PluralRules *rules = PluralRules::createRules("a: n mod 10 in 5..8", status); if (!rules) { errln("couldn't instantiate rules"); return; } UnicodeString keyword = rules->select((int32_t)26); if (keyword != "a") { errln("expected 'a' for 26 but didn't get it."); } keyword = rules->select(26.5); if (keyword != "other") { errln("expected 'other' for 26.5 but didn't get it."); } delete rules; }
ECode CResources::GetQuantityText( /* [in] */ Int32 id, /* [in] */ Int32 quantity, /* [out] */ ICharSequence** csq) { if (!csq) { return E_INVALID_ARGUMENT; } PluralRules* rule = GetPluralRule(); mAssets->GetResourceBagText(id, rule->AttrForNumber(quantity), csq); if (*csq != NULL) { return NOERROR; } mAssets->GetResourceBagText(id, PluralRules::ID_OTHER, csq); if (*csq != NULL) { return NOERROR; } Logger::E(TAG, StringBuffer("Plural resource ID #0x") + id + " quantity=" + quantity + " item=" + PluralRules::StringForQuantity(rule->QuantityForNumber(quantity))); return E_NOT_FOUND_EXCEPTION; }
UBool testEquality(const PluralRules &test) { UnicodeString testEquRules[MAX_EQ_ROW][MAX_EQ_COL] = { { UNICODE_STRING_SIMPLE("a: n in 2..3"), UNICODE_STRING_SIMPLE("a: n is 2 or n is 3"), UNICODE_STRING_SIMPLE( "a:n is 3 and n in 2..5 or n is 2"), "", }, { UNICODE_STRING_SIMPLE("a: n is 12; b:n mod 10 in 2..3"), UNICODE_STRING_SIMPLE("b: n mod 10 in 2..3 and n is not 12; a: n in 12..12"), UNICODE_STRING_SIMPLE("b: n is 13; a: n in 12..13; b: n mod 10 is 2 or n mod 10 is 3"), "", } }; UErrorCode status = U_ZERO_ERROR; UnicodeString key[MAX_EQ_COL]; UBool ret=TRUE; for (int32_t i=0; i<MAX_EQ_ROW; ++i) { PluralRules* rules[MAX_EQ_COL]; for (int32_t j=0; j<MAX_EQ_COL; ++j) { rules[j]=NULL; } int32_t totalRules=0; while((totalRules<MAX_EQ_COL) && (testEquRules[i][totalRules].length()>0) ) { rules[totalRules]=test.createRules(testEquRules[i][totalRules], status); totalRules++; } for (int32_t n=0; n<300 && ret ; ++n) { for(int32_t j=0; j<totalRules;++j) { key[j] = rules[j]->select(n); } for(int32_t j=0; j<totalRules-1;++j) { if (key[j]!=key[j+1]) { ret= FALSE; break; } } } for (int32_t j=0; j<MAX_EQ_COL; ++j) { if (rules[j]!=NULL) { delete rules[j]; } } } return ret; }
void PluralRulesTest::testAvailbleLocales() { // Hash set of (char *) strings. UErrorCode status = U_ZERO_ERROR; UHashtable *localeSet = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, uhash_compareLong, &status); uhash_setKeyDeleter(localeSet, uprv_deleteUObject); if (U_FAILURE(status)) { errln("file %s, line %d: Error status = %s", __FILE__, __LINE__, u_errorName(status)); return; } // Check that each locale returned by the iterator is unique. StringEnumeration *localesEnum = PluralRules::getAvailableLocales(status); int localeCount = 0; for (;;) { const char *locale = localesEnum->next(NULL, status); if (U_FAILURE(status)) { dataerrln("file %s, line %d: Error status = %s", __FILE__, __LINE__, u_errorName(status)); return; } if (locale == NULL) { break; } localeCount++; int32_t oldVal = uhash_puti(localeSet, new UnicodeString(locale), 1, &status); if (oldVal != 0) { errln("file %s, line %d: locale %s was seen before.", __FILE__, __LINE__, locale); } } // Reset the iterator, verify that we get the same count. localesEnum->reset(status); int32_t localeCount2 = 0; while (localesEnum->next(NULL, status) != NULL) { if (U_FAILURE(status)) { errln("file %s, line %d: Error status = %s", __FILE__, __LINE__, u_errorName(status)); break; } localeCount2++; } if (localeCount != localeCount2) { errln("file %s, line %d: locale counts differ. They are (%d, %d)", __FILE__, __LINE__, localeCount, localeCount2); } // Instantiate plural rules for each available locale. localesEnum->reset(status); for (;;) { status = U_ZERO_ERROR; const char *localeName = localesEnum->next(NULL, status); if (U_FAILURE(status)) { errln("file %s, line %d: Error status = %s, locale = %s", __FILE__, __LINE__, u_errorName(status), localeName); return; } if (localeName == NULL) { break; } Locale locale = Locale::createFromName(localeName); PluralRules *pr = PluralRules::forLocale(locale, status); if (U_FAILURE(status)) { errln("file %s, line %d: Error %s creating plural rules for locale %s", __FILE__, __LINE__, u_errorName(status), localeName); continue; } if (pr == NULL) { errln("file %s, line %d: Null plural rules for locale %s", __FILE__, __LINE__, localeName); continue; } // Pump some numbers through the plural rules. Can't check for correct results, // mostly this to tickle any asserts or crashes that may be lurking. for (double n=0; n<120.0; n+=0.5) { UnicodeString keyword = pr->select(n); if (keyword.length() == 0) { errln("file %s, line %d, empty keyword for n = %g, locale %s", __FILE__, __LINE__, n, localeName); } } delete pr; } uhash_close(localeSet); delete localesEnum; }
void PluralRulesTest::testGetAllKeywordValues() { const char* data[] = { "a: n in 2..5", "a: 2,3,4,5; other: null; b:", "a: n not in 2..5", "a: null; other: null", "a: n within 2..5", "a: null; other: null", "a: n not within 2..5", "a: null; other: null", "a: n in 2..5 or n within 6..8", "a: null", // ignore 'other' here on out, always null "a: n in 2..5 and n within 6..8", "a:", "a: n in 2..5 and n within 5..8", "a: 5", "a: n within 2..5 and n within 6..8", "a:", // our sampling catches these "a: n within 2..5 and n within 5..8", "a: 5", // '' "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5", "a: 2,4", "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5 " "or n within 5..6 and n within 6..7", "a: null", // but not this... "a: n mod 3 is 0", "a: null", "a: n mod 3 is 0 and n within 1..2", "a:", "a: n mod 3 is 0 and n within 0..5", "a: 0,3", "a: n mod 3 is 0 and n within 0..6", "a: null", // similarly with mod, we don't catch... "a: n mod 3 is 0 and n in 3..12", "a: 3,6,9,12", NULL }; for (int i = 0; data[i] != NULL; i += 2) { UErrorCode status = U_ZERO_ERROR; UnicodeString ruleDescription(data[i], -1, US_INV); const char* result = data[i+1]; logln("[%d] %s", i >> 1, data[i]); PluralRules *p = PluralRules::createRules(ruleDescription, status); if (p == NULL || U_FAILURE(status)) { errln("file %s, line %d: could not create rules from '%s'\n" " ErrorCode: %s\n", __FILE__, __LINE__, data[i], u_errorName(status)); continue; } // TODO: fix samples implementation, re-enable test. (void)result; #if 0 const char* rp = result; while (*rp) { while (*rp == ' ') ++rp; if (!rp) { break; } const char* ep = rp; while (*ep && *ep != ':') ++ep; status = U_ZERO_ERROR; UnicodeString keyword(rp, ep - rp, US_INV); double samples[4]; // no test above should have more samples than 4 int32_t count = p->getAllKeywordValues(keyword, &samples[0], 4, status); if (U_FAILURE(status)) { errln("error getting samples for %s", rp); break; } if (count > 4) { errln("count > 4 for keyword %s", rp); count = 4; } if (*ep) { ++ep; // skip colon while (*ep && *ep == ' ') ++ep; // and spaces } UBool ok = TRUE; if (count == -1) { if (*ep != 'n') { errln("expected values for keyword %s but got -1 (%s)", rp, ep); ok = FALSE; } } else if (*ep == 'n') { errln("expected count of -1, got %d, for keyword %s (%s)", count, rp, ep); ok = FALSE; } // We'll cheat a bit here. The samples happend to be in order and so are our // expected values, so we'll just test in order until a failure. If the // implementation changes to return samples in an arbitrary order, this test // must change. There's no actual restriction on the order of the samples. for (int j = 0; ok && j < count; ++j ) { // we've verified count < 4 double val = samples[j]; if (*ep == 0 || *ep == ';') { errln("got unexpected value[%d]: %g", j, val); ok = FALSE; break; } char* xp; double expectedVal = strtod(ep, &xp); if (xp == ep) { // internal error errln("yikes!"); ok = FALSE; break; } ep = xp; if (expectedVal != val) { errln("expected %g but got %g", expectedVal, val); ok = FALSE; break; } if (*ep == ',') ++ep; } if (ok && count != -1) { if (!(*ep == 0 || *ep == ';')) { errln("file: %s, line %d, didn't get expected value: %s", __FILE__, __LINE__, ep); ok = FALSE; } } while (*ep && *ep != ';') ++ep; if (*ep == ';') ++ep; rp = ep; } #endif delete p; } }