mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 06:25:30 +00:00
ICU-21947 Replace FixedDecimal with DecimalQuantity in PluralRule sample parsing
See #2007
This commit is contained in:
parent
0eecb25011
commit
3ef03a4087
9 changed files with 755 additions and 641 deletions
|
@ -26,6 +26,7 @@
|
|||
#include "hash.h"
|
||||
#include "locutil.h"
|
||||
#include "mutex.h"
|
||||
#include "number_decnum.h"
|
||||
#include "patternprops.h"
|
||||
#include "plurrule_impl.h"
|
||||
#include "putilimp.h"
|
||||
|
@ -45,7 +46,9 @@
|
|||
U_NAMESPACE_BEGIN
|
||||
|
||||
using namespace icu::pluralimpl;
|
||||
using icu::number::impl::DecNum;
|
||||
using icu::number::impl::DecimalQuantity;
|
||||
using icu::number::impl::RoundingMode;
|
||||
|
||||
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};
|
||||
|
@ -369,36 +372,18 @@ PluralRules::getAllKeywordValues(const UnicodeString & /* keyword */, double * /
|
|||
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 const double powers10[7] = {1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0}; // powers of 10 for 0..6
|
||||
static double applyExponent(double source, int32_t exponent) {
|
||||
if (exponent >= 0 && exponent <= 6) {
|
||||
return source * powers10[exponent];
|
||||
}
|
||||
return source * pow(10.0, exponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for the overrides of getSamples() for double and FixedDecimal
|
||||
* return value types. Provide only one of an allocated array of doubles or
|
||||
* FixedDecimals, and a nullptr for the other.
|
||||
* Helper method for the overrides of getSamples() for double and DecimalQuantity
|
||||
* return value types. Provide only one of an allocated array of double or
|
||||
* DecimalQuantity, and a nullptr for the other.
|
||||
*/
|
||||
static int32_t
|
||||
getSamplesFromString(const UnicodeString &samples, double *destDbl,
|
||||
FixedDecimal* destFd, int32_t destCapacity,
|
||||
DecimalQuantity* destDq, int32_t destCapacity,
|
||||
UErrorCode& status) {
|
||||
|
||||
if ((destDbl == nullptr && destFd == nullptr)
|
||||
|| (destDbl != nullptr && destFd != nullptr)) {
|
||||
if ((destDbl == nullptr && destDq == nullptr)
|
||||
|| (destDbl != nullptr && destDq != nullptr)) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return 0;
|
||||
}
|
||||
|
@ -420,58 +405,75 @@ getSamplesFromString(const UnicodeString &samples, double *destDbl,
|
|||
// std::cout << "PluralRules::getSamples(), samplesRange = \"" << sampleRange.toUTF8String(ss) << "\"\n";
|
||||
int32_t tildeIndex = sampleRange.indexOf(TILDE);
|
||||
if (tildeIndex < 0) {
|
||||
FixedDecimal fixed(sampleRange, status);
|
||||
DecimalQuantity dq = DecimalQuantity::fromExponentString(sampleRange, status);
|
||||
if (isDouble) {
|
||||
double sampleValue = fixed.source;
|
||||
if (fixed.visibleDecimalDigitCount == 0 || sampleValue != floor(sampleValue)) {
|
||||
destDbl[sampleCount++] = applyExponent(sampleValue, fixed.exponent);
|
||||
// See warning note below about lack of precision for floating point samples for numbers with
|
||||
// trailing zeroes in the decimal fraction representation.
|
||||
double dblValue = dq.toDouble();
|
||||
if (!(dblValue == floor(dblValue) && dq.fractionCount() > 0)) {
|
||||
destDbl[sampleCount++] = dblValue;
|
||||
}
|
||||
} else {
|
||||
destFd[sampleCount++] = fixed;
|
||||
destDq[sampleCount++] = dq;
|
||||
}
|
||||
} else {
|
||||
FixedDecimal fixedLo(sampleRange.tempSubStringBetween(0, tildeIndex), status);
|
||||
FixedDecimal fixedHi(sampleRange.tempSubStringBetween(tildeIndex+1), status);
|
||||
double rangeLo = fixedLo.source;
|
||||
double rangeHi = fixedHi.source;
|
||||
DecimalQuantity rangeLo =
|
||||
DecimalQuantity::fromExponentString(sampleRange.tempSubStringBetween(0, tildeIndex), status);
|
||||
DecimalQuantity rangeHi = DecimalQuantity::fromExponentString(sampleRange.tempSubStringBetween(tildeIndex+1), status);
|
||||
if (U_FAILURE(status)) {
|
||||
break;
|
||||
}
|
||||
if (rangeHi < rangeLo) {
|
||||
if (rangeHi.toDouble() < rangeLo.toDouble()) {
|
||||
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.
|
||||
DecimalQuantity incrementDq;
|
||||
incrementDq.setToInt(1);
|
||||
int32_t lowerDispMag = rangeLo.getLowerDisplayMagnitude();
|
||||
int32_t exponent = rangeLo.getExponent();
|
||||
int32_t incrementScale = lowerDispMag + exponent;
|
||||
incrementDq.adjustMagnitude(incrementScale);
|
||||
double incrementVal = incrementDq.toDouble(); // 10 ^ incrementScale
|
||||
|
||||
|
||||
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) {
|
||||
double sampleValue = n/scale;
|
||||
DecimalQuantity dq(rangeLo);
|
||||
double dblValue = dq.toDouble();
|
||||
double end = rangeHi.toDouble();
|
||||
|
||||
while (dblValue <= end) {
|
||||
if (isDouble) {
|
||||
// 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.
|
||||
if (!(sampleValue == floor(sampleValue) && fixedLo.visibleDecimalDigitCount > 0)) {
|
||||
destDbl[sampleCount++] = sampleValue;
|
||||
if (!(dblValue == floor(dblValue) && dq.fractionCount() > 0)) {
|
||||
destDbl[sampleCount++] = dblValue;
|
||||
}
|
||||
} else {
|
||||
int32_t v = (int32_t) fixedLo.getPluralOperand(PluralOperand::PLURAL_OPERAND_V);
|
||||
int32_t e = (int32_t) fixedLo.getPluralOperand(PluralOperand::PLURAL_OPERAND_E);
|
||||
FixedDecimal newSample = FixedDecimal::createWithExponent(sampleValue, v, e);
|
||||
destFd[sampleCount++] = newSample;
|
||||
destDq[sampleCount++] = dq;
|
||||
}
|
||||
if (sampleCount >= destCapacity) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Increment dq for next iteration
|
||||
|
||||
// Because DecNum and DecimalQuantity do not support
|
||||
// add operations, we need to convert to/from double,
|
||||
// despite precision lossiness for decimal fractions like 0.1.
|
||||
dblValue += incrementVal;
|
||||
DecNum newDqDecNum;
|
||||
newDqDecNum.setTo(dblValue, status);
|
||||
DecimalQuantity newDq;
|
||||
newDq.setToDecNum(newDqDecNum, status);
|
||||
newDq.setMinFraction(-lowerDispMag);
|
||||
newDq.roundToMagnitude(lowerDispMag, RoundingMode::UNUM_ROUND_HALFEVEN, status);
|
||||
newDq.adjustMagnitude(-exponent);
|
||||
newDq.adjustExponent(exponent);
|
||||
dblValue = newDq.toDouble();
|
||||
dq = newDq;
|
||||
}
|
||||
}
|
||||
sampleStartIdx = sampleEndIdx + 1;
|
||||
|
@ -505,7 +507,7 @@ PluralRules::getSamples(const UnicodeString &keyword, double *dest,
|
|||
}
|
||||
|
||||
int32_t
|
||||
PluralRules::getSamples(const UnicodeString &keyword, FixedDecimal *dest,
|
||||
PluralRules::getSamples(const UnicodeString &keyword, DecimalQuantity *dest,
|
||||
int32_t destCapacity, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return 0;
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
* A FixedDecimal version of UPLRULES_NO_UNIQUE_VALUE used in PluralRulesTest
|
||||
* for parsing of samples.
|
||||
*/
|
||||
#define UPLRULES_NO_UNIQUE_VALUE_DECIMAL (FixedDecimal((double)-0.00123456777))
|
||||
#define UPLRULES_NO_UNIQUE_VALUE_DECIMAL(ERROR_CODE) (DecimalQuantity::fromExponentString(u"-0.00123456777", ERROR_CODE))
|
||||
|
||||
class PluralRulesTest;
|
||||
|
||||
|
|
|
@ -59,9 +59,15 @@ class FormattedNumber;
|
|||
class FormattedNumberRange;
|
||||
namespace impl {
|
||||
class UFormattedNumberRangeData;
|
||||
class DecimalQuantity;
|
||||
class DecNum;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
using icu::number::impl::DecimalQuantity;
|
||||
#endif /* U_HIDE_INTERNAL_API */
|
||||
|
||||
/**
|
||||
* Defines rules for mapping non-negative numeric values onto a small set of
|
||||
* keywords. Rules are constructed from a text description, consisting
|
||||
|
@ -468,7 +474,7 @@ public:
|
|||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/**
|
||||
* Internal-only function that returns FixedDecimals instead of doubles.
|
||||
* Internal-only function that returns DecimalQuantitys instead of doubles.
|
||||
*
|
||||
* Returns sample values for which select() would return the keyword. If
|
||||
* the keyword is unknown, returns no values, but this is not an error.
|
||||
|
@ -488,7 +494,7 @@ public:
|
|||
* @internal
|
||||
*/
|
||||
int32_t getSamples(const UnicodeString &keyword,
|
||||
FixedDecimal *dest, int32_t destCapacity,
|
||||
DecimalQuantity *dest, int32_t destCapacity,
|
||||
UErrorCode& status);
|
||||
#endif /* U_HIDE_INTERNAL_API */
|
||||
|
||||
|
|
|
@ -50,7 +50,9 @@ void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &na
|
|||
TESTCASE_AUTO(testAPI);
|
||||
// TESTCASE_AUTO(testGetUniqueKeywordValue);
|
||||
TESTCASE_AUTO(testGetSamples);
|
||||
TESTCASE_AUTO(testGetFixedDecimalSamples);
|
||||
TESTCASE_AUTO(testGetDecimalQuantitySamples);
|
||||
TESTCASE_AUTO(testGetOrAddSamplesFromString);
|
||||
TESTCASE_AUTO(testGetOrAddSamplesFromStringCompactNotation);
|
||||
TESTCASE_AUTO(testSamplesWithExponent);
|
||||
TESTCASE_AUTO(testSamplesWithCompactNotation);
|
||||
TESTCASE_AUTO(testWithin);
|
||||
|
@ -396,9 +398,16 @@ void PluralRulesTest::testGetUniqueKeywordValue() {
|
|||
assertRuleKeyValue("a: n is 1", "other", UPLRULES_NO_UNIQUE_VALUE); // key matches default rule
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the double API for getting plural samples, assert all samples match the keyword
|
||||
* they are listed under, for all locales.
|
||||
*
|
||||
* Specifically, iterate over all locales, get plural rules for the locale, iterate over every rule,
|
||||
* then iterate over every sample in the rule, parse sample to a number (double), use that number
|
||||
* as an input to .select() for the rules object, and assert the actual return plural keyword matches
|
||||
* what we expect based on the plural rule string.
|
||||
*/
|
||||
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;
|
||||
|
@ -457,21 +466,24 @@ void PluralRulesTest::testGetSamples() {
|
|||
}
|
||||
}
|
||||
|
||||
void PluralRulesTest::testGetFixedDecimalSamples() {
|
||||
// TODO: fix samples, re-enable this test.
|
||||
|
||||
/**
|
||||
* Using the DecimalQuantity API for getting plural samples, assert all samples match the keyword
|
||||
* they are listed under, for all locales.
|
||||
*
|
||||
* Specifically, iterate over all locales, get plural rules for the locale, iterate over every rule,
|
||||
* then iterate over every sample in the rule, parse sample to a number (DecimalQuantity), use that number
|
||||
* as an input to .select() for the rules object, and assert the actual return plural keyword matches
|
||||
* what we expect based on the plural rule string.
|
||||
*/
|
||||
void PluralRulesTest::testGetDecimalQuantitySamples() {
|
||||
// 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);
|
||||
|
||||
FixedDecimal values[1000];
|
||||
DecimalQuantity values[1000];
|
||||
for (int32_t i = 0; U_SUCCESS(status) && i < numLocales; ++i) {
|
||||
//if (uprv_strcmp(locales[i].getLanguage(), "fr") == 0 &&
|
||||
// logKnownIssue("21322", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
|
||||
// continue;
|
||||
//}
|
||||
LocalPointer<PluralRules> rules(PluralRules::forLocale(locales[i], status));
|
||||
if (U_FAILURE(status)) {
|
||||
break;
|
||||
|
@ -501,21 +513,24 @@ void PluralRulesTest::testGetFixedDecimalSamples() {
|
|||
count = UPRV_LENGTHOF(values);
|
||||
}
|
||||
for (int32_t j = 0; j < count; ++j) {
|
||||
if (values[j] == UPLRULES_NO_UNIQUE_VALUE_DECIMAL) {
|
||||
if (values[j] == UPLRULES_NO_UNIQUE_VALUE_DECIMAL(status)) {
|
||||
errln("got 'no unique value' among values");
|
||||
} else {
|
||||
if (U_FAILURE(status)){
|
||||
errln(UnicodeString(u"getSamples() failed for sample ") +
|
||||
values[j].toExponentString() +
|
||||
UnicodeString(u", keyword ") + *keyword);
|
||||
continue;
|
||||
}
|
||||
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) {
|
||||
if (values[j].exponent == 0 || !logKnownIssue("21714", "PluralRules::select treats 1c6 as 1")) {
|
||||
UnicodeString valueString(values[j].toString());
|
||||
char valueBuf[16];
|
||||
valueString.extract(0, valueString.length(), valueBuf, sizeof(valueBuf));
|
||||
errln("file %s, line %d, Locale %s, sample for keyword \"%s\": %s, select(%s) returns keyword \"%s\"",
|
||||
__FILE__, __LINE__, locales[i].getName(), US(*keyword).cstr(), valueBuf, valueBuf, US(resultKeyword).cstr());
|
||||
}
|
||||
errln("file %s, line %d, Locale %s, sample for keyword \"%s\": %s, select(%s) returns keyword \"%s\"",
|
||||
__FILE__, __LINE__, locales[i].getName(), US(*keyword).cstr(),
|
||||
US(values[j].toExponentString()).cstr(), US(values[j].toExponentString()).cstr(),
|
||||
US(resultKeyword).cstr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -523,6 +538,102 @@ void PluralRulesTest::testGetFixedDecimalSamples() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test addSamples (Java) / getSamplesFromString (C++) to ensure the expansion of plural rule sample range
|
||||
* expands to a sequence of sample numbers that is incremented as the right scale.
|
||||
*
|
||||
* Do this for numbers with fractional digits but no exponent.
|
||||
*/
|
||||
void PluralRulesTest::testGetOrAddSamplesFromString() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UnicodeString description(u"testkeyword: e != 0 @decimal 2.0c6~4.0c6, …");
|
||||
LocalPointer<PluralRules> rules(PluralRules::createRules(description, status));
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Couldn't create plural rules from a string, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
|
||||
LocalPointer<StringEnumeration> keywords(rules->getKeywords(status));
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Couldn't get keywords from a parsed rules object, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
|
||||
DecimalQuantity values[1000];
|
||||
const UnicodeString keyword(u"testkeyword");
|
||||
int32_t count = rules->getSamples(keyword, values, UPRV_LENGTHOF(values), status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln(UnicodeString(u"getSamples() failed for plural rule keyword ") + keyword);
|
||||
return;
|
||||
}
|
||||
|
||||
UnicodeString expDqStrs[] = {
|
||||
u"2.0c6", u"2.1c6", u"2.2c6", u"2.3c6", u"2.4c6", u"2.5c6", u"2.6c6", u"2.7c6", u"2.8c6", u"2.9c6",
|
||||
u"3.0c6", u"3.1c6", u"3.2c6", u"3.3c6", u"3.4c6", u"3.5c6", u"3.6c6", u"3.7c6", u"3.8c6", u"3.9c6",
|
||||
u"4.0c6"
|
||||
};
|
||||
assertEquals(u"Number of parsed samples from test string incorrect", 21, count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
UnicodeString expDqStr = expDqStrs[i];
|
||||
DecimalQuantity sample = values[i];
|
||||
UnicodeString sampleStr = sample.toExponentString();
|
||||
|
||||
assertEquals(u"Expansion of sample range to sequence of sample values should increment at the right scale",
|
||||
expDqStr, sampleStr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test addSamples (Java) / getSamplesFromString (C++) to ensure the expansion of plural rule sample range
|
||||
* expands to a sequence of sample numbers that is incremented as the right scale.
|
||||
*
|
||||
* Do this for numbers written in a notation that has an exponent, for which the number is an
|
||||
* integer (also as defined in the UTS 35 spec for the plural operands) but whose representation
|
||||
* has fractional digits in the significand written before the exponent.
|
||||
*/
|
||||
void PluralRulesTest::testGetOrAddSamplesFromStringCompactNotation() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
UnicodeString description(u"testkeyword: e != 0 @decimal 2.0~4.0, …");
|
||||
LocalPointer<PluralRules> rules(PluralRules::createRules(description, status));
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Couldn't create plural rules from a string, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
|
||||
LocalPointer<StringEnumeration> keywords(rules->getKeywords(status));
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Couldn't get keywords from a parsed rules object, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
|
||||
DecimalQuantity values[1000];
|
||||
const UnicodeString keyword(u"testkeyword");
|
||||
int32_t count = rules->getSamples(keyword, values, UPRV_LENGTHOF(values), status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln(UnicodeString(u"getSamples() failed for plural rule keyword ") + keyword);
|
||||
return;
|
||||
}
|
||||
|
||||
UnicodeString expDqStrs[] = {
|
||||
u"2.0", u"2.1", u"2.2", u"2.3", u"2.4", u"2.5", u"2.6", u"2.7", u"2.8", u"2.9",
|
||||
u"3.0", u"3.1", u"3.2", u"3.3", u"3.4", u"3.5", u"3.6", u"3.7", u"3.8", u"3.9",
|
||||
u"4.0"
|
||||
};
|
||||
assertEquals(u"Number of parsed samples from test string incorrect", 21, count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
UnicodeString expDqStr = expDqStrs[i];
|
||||
DecimalQuantity sample = values[i];
|
||||
UnicodeString sampleStr = sample.toExponentString();
|
||||
|
||||
assertEquals(u"Expansion of sample range to sequence of sample values should increment at the right scale",
|
||||
expDqStr, sampleStr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test is for the support of X.YeZ scientific notation of numbers in
|
||||
* the plural sample string.
|
||||
*/
|
||||
void PluralRulesTest::testSamplesWithExponent() {
|
||||
// integer samples
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
@ -538,9 +649,9 @@ void PluralRulesTest::testSamplesWithExponent() {
|
|||
errln("Couldn't create plural rules from a string using exponent notation, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
checkNewSamples(description, test, u"one", u"@integer 0, 1, 1e5", FixedDecimal(0));
|
||||
checkNewSamples(description, test, u"many", u"@integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, …", FixedDecimal(1000000));
|
||||
checkNewSamples(description, test, u"other", u"@integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …", FixedDecimal(2));
|
||||
checkNewSamples(description, test, u"one", u"@integer 0, 1, 1e5", DecimalQuantity::fromExponentString(u"0", status));
|
||||
checkNewSamples(description, test, u"many", u"@integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, …", DecimalQuantity::fromExponentString(u"1000000", status));
|
||||
checkNewSamples(description, test, u"other", u"@integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …", DecimalQuantity::fromExponentString(u"2", status));
|
||||
|
||||
// decimal samples
|
||||
status = U_ZERO_ERROR;
|
||||
|
@ -555,12 +666,15 @@ void PluralRulesTest::testSamplesWithExponent() {
|
|||
errln("Couldn't create plural rules from a string using exponent notation, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
checkNewSamples(description2, test2, u"one", u"@decimal 0.0~1.5, 1.1e5", FixedDecimal(0, 1));
|
||||
checkNewSamples(description2, test2, u"many", u"@decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …", FixedDecimal::createWithExponent(2.1, 1, 6));
|
||||
checkNewSamples(description2, test2, u"other", u"@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …", FixedDecimal(2.0, 1));
|
||||
checkNewSamples(description2, test2, u"one", u"@decimal 0.0~1.5, 1.1e5", DecimalQuantity::fromExponentString(u"0.0", status));
|
||||
checkNewSamples(description2, test2, u"many", u"@decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …", DecimalQuantity::fromExponentString(u"2.1c6", status));
|
||||
checkNewSamples(description2, test2, u"other", u"@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …", DecimalQuantity::fromExponentString(u"2.0", status));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This test is for the support of X.YcZ compact notation of numbers in
|
||||
* the plural sample string.
|
||||
*/
|
||||
void PluralRulesTest::testSamplesWithCompactNotation() {
|
||||
// integer samples
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
@ -576,9 +690,9 @@ void PluralRulesTest::testSamplesWithCompactNotation() {
|
|||
errln("Couldn't create plural rules from a string using exponent notation, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
checkNewSamples(description, test, u"one", u"@integer 0, 1, 1c5", FixedDecimal(0));
|
||||
checkNewSamples(description, test, u"many", u"@integer 1000000, 2c6, 3c6, 4c6, 5c6, 6c6, 7c6, …", FixedDecimal(1000000));
|
||||
checkNewSamples(description, test, u"other", u"@integer 2~17, 100, 1000, 10000, 100000, 2c5, 3c5, 4c5, 5c5, 6c5, 7c5, …", FixedDecimal(2));
|
||||
checkNewSamples(description, test, u"one", u"@integer 0, 1, 1c5", DecimalQuantity::fromExponentString(u"0", status));
|
||||
checkNewSamples(description, test, u"many", u"@integer 1000000, 2c6, 3c6, 4c6, 5c6, 6c6, 7c6, …", DecimalQuantity::fromExponentString(u"1000000", status));
|
||||
checkNewSamples(description, test, u"other", u"@integer 2~17, 100, 1000, 10000, 100000, 2c5, 3c5, 4c5, 5c5, 6c5, 7c5, …", DecimalQuantity::fromExponentString(u"2", status));
|
||||
|
||||
// decimal samples
|
||||
status = U_ZERO_ERROR;
|
||||
|
@ -593,9 +707,9 @@ void PluralRulesTest::testSamplesWithCompactNotation() {
|
|||
errln("Couldn't create plural rules from a string using exponent notation, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
checkNewSamples(description2, test2, u"one", u"@decimal 0.0~1.5, 1.1c5", FixedDecimal(0, 1));
|
||||
checkNewSamples(description2, test2, u"many", u"@decimal 2.1c6, 3.1c6, 4.1c6, 5.1c6, 6.1c6, 7.1c6, …", FixedDecimal::createWithExponent(2.1, 1, 6));
|
||||
checkNewSamples(description2, test2, u"other", u"@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1c5, 3.1c5, 4.1c5, 5.1c5, 6.1c5, 7.1c5, …", FixedDecimal(2.0, 1));
|
||||
checkNewSamples(description2, test2, u"one", u"@decimal 0.0~1.5, 1.1c5", DecimalQuantity::fromExponentString(u"0.0", status));
|
||||
checkNewSamples(description2, test2, u"many", u"@decimal 2.1c6, 3.1c6, 4.1c6, 5.1c6, 6.1c6, 7.1c6, …", DecimalQuantity::fromExponentString(u"2.1c6", status));
|
||||
checkNewSamples(description2, test2, u"other", u"@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1c5, 3.1c5, 4.1c5, 5.1c5, 6.1c5, 7.1c5, …", DecimalQuantity::fromExponentString(u"2.0", status));
|
||||
}
|
||||
|
||||
void PluralRulesTest::checkNewSamples(
|
||||
|
@ -603,17 +717,17 @@ void PluralRulesTest::checkNewSamples(
|
|||
const LocalPointer<PluralRules> &test,
|
||||
UnicodeString keyword,
|
||||
UnicodeString samplesString,
|
||||
FixedDecimal firstInRange) {
|
||||
DecimalQuantity firstInRange) {
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
FixedDecimal samples[1000];
|
||||
DecimalQuantity samples[1000];
|
||||
|
||||
test->getSamples(keyword, samples, UPRV_LENGTHOF(samples), status);
|
||||
if (U_FAILURE(status)) {
|
||||
errln("Couldn't retrieve plural samples, with error = %s", u_errorName(status));
|
||||
return;
|
||||
}
|
||||
FixedDecimal actualFirstSample = samples[0];
|
||||
DecimalQuantity actualFirstSample = samples[0];
|
||||
|
||||
if (!(firstInRange == actualFirstSample)) {
|
||||
CStr descCstr(description);
|
||||
|
@ -776,6 +890,11 @@ PluralRulesTest::testGetAllKeywordValues() {
|
|||
|
||||
// For the time being, the compact notation exponent operand `c` is an alias
|
||||
// for the scientific exponent operand `e` and compact notation.
|
||||
/**
|
||||
* Test the proper plural rule keyword selection given an input number that is
|
||||
* already formatted into scientific notation. This exercises the `e` plural operand
|
||||
* for the formatted number.
|
||||
*/
|
||||
void
|
||||
PluralRulesTest::testScientificPluralKeyword() {
|
||||
IcuTestErrorCode errorCode(*this, "testScientificPluralKeyword");
|
||||
|
@ -838,6 +957,11 @@ PluralRulesTest::testScientificPluralKeyword() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the proper plural rule keyword selection given an input number that is
|
||||
* already formatted into compact notation. This exercises the `c` plural operand
|
||||
* for the formatted number.
|
||||
*/
|
||||
void
|
||||
PluralRulesTest::testCompactDecimalPluralKeyword() {
|
||||
IcuTestErrorCode errorCode(*this, "testCompactDecimalPluralKeyword");
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "intltest.h"
|
||||
#include "number_decimalquantity.h"
|
||||
#include "unicode/localpointer.h"
|
||||
#include "unicode/plurrule.h"
|
||||
|
||||
|
@ -30,7 +31,9 @@ private:
|
|||
void testAPI();
|
||||
void testGetUniqueKeywordValue();
|
||||
void testGetSamples();
|
||||
void testGetFixedDecimalSamples();
|
||||
void testGetDecimalQuantitySamples();
|
||||
void testGetOrAddSamplesFromString();
|
||||
void testGetOrAddSamplesFromStringCompactNotation();
|
||||
void testSamplesWithExponent();
|
||||
void testSamplesWithCompactNotation();
|
||||
void testWithin();
|
||||
|
@ -55,7 +58,7 @@ private:
|
|||
const LocalPointer<PluralRules> &test,
|
||||
UnicodeString keyword,
|
||||
UnicodeString samplesString,
|
||||
FixedDecimal firstInRange);
|
||||
::icu::number::impl::DecimalQuantity firstInRange);
|
||||
UnicodeString getPluralKeyword(const LocalPointer<PluralRules> &rules,
|
||||
Locale locale, double number, const char16_t* skeleton);
|
||||
void checkSelect(const LocalPointer<PluralRules> &rules, UErrorCode &status,
|
||||
|
|
|
@ -15,21 +15,22 @@ import java.io.ObjectInputStream;
|
|||
import java.io.ObjectOutputStream;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.ibm.icu.impl.PluralRulesLoader;
|
||||
import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
|
||||
import com.ibm.icu.impl.number.range.StandardPluralRanges;
|
||||
import com.ibm.icu.number.FormattedNumber;
|
||||
import com.ibm.icu.number.FormattedNumberRange;
|
||||
|
@ -329,6 +330,16 @@ public class PluralRules implements Serializable {
|
|||
*/
|
||||
public static final double NO_UNIQUE_VALUE = -0.00123456777;
|
||||
|
||||
/**
|
||||
* Value returned by {@link #getUniqueKeywordDecimalQuantityValue} when there is no
|
||||
* unique value to return.
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final DecimalQuantity NO_UNIQUE_VALUE_DECIMAL_QUANTITY =
|
||||
new DecimalQuantity_DualStorageBCD(-0.00123456777);
|
||||
|
||||
/**
|
||||
* Type of plurals and PluralRules.
|
||||
* @stable ICU 50
|
||||
|
@ -867,50 +878,6 @@ public class PluralRules implements Serializable {
|
|||
this.baseFactor = other.baseFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public FixedDecimal (String n) {
|
||||
// Ugly, but for samples we don't care.
|
||||
this(parseDecimalSampleRangeNumString(n));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only
|
||||
*/
|
||||
@Deprecated
|
||||
private static FixedDecimal parseDecimalSampleRangeNumString(String num) {
|
||||
if (num.contains("e") || num.contains("c")) {
|
||||
int ePos = num.lastIndexOf('e');
|
||||
if (ePos < 0) {
|
||||
ePos = num.lastIndexOf('c');
|
||||
}
|
||||
int expNumPos = ePos + 1;
|
||||
String exponentStr = num.substring(expNumPos);
|
||||
int exponent = Integer.parseInt(exponentStr);
|
||||
String fractionStr = num.substring(0, ePos);
|
||||
return FixedDecimal.createWithExponent(
|
||||
Double.parseDouble(fractionStr),
|
||||
getVisibleFractionCount(fractionStr),
|
||||
exponent);
|
||||
} else {
|
||||
return new FixedDecimal(Double.parseDouble(num), getVisibleFractionCount(num));
|
||||
}
|
||||
}
|
||||
|
||||
private static int getVisibleFractionCount(String value) {
|
||||
value = value.trim();
|
||||
int decimalPos = value.indexOf('.') + 1;
|
||||
if (decimalPos == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return value.length() - decimalPos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -1070,19 +1037,6 @@ public class PluralRules implements Serializable {
|
|||
return (isNegative ? -source : source) * Math.pow(10, exponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public long getShiftedValue() {
|
||||
if (exponent != 0 && visibleDecimalDigitCount == 0 && decimalDigits == 0) {
|
||||
// Need to take exponent into account if we have it
|
||||
return (long)(source * Math.pow(10, exponent));
|
||||
}
|
||||
return integerValue * baseFactor + decimalDigits;
|
||||
}
|
||||
|
||||
private void writeObject(
|
||||
ObjectOutputStream out)
|
||||
throws IOException {
|
||||
|
@ -1141,31 +1095,32 @@ public class PluralRules implements Serializable {
|
|||
}
|
||||
|
||||
/**
|
||||
* A range of NumberInfo that includes all values with the same visibleFractionDigitCount.
|
||||
* A range of DecimalQuantity representing PluralRules samples that includes
|
||||
* all values with the same visibleFractionDigitCount.
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static class FixedDecimalRange {
|
||||
public static class DecimalQuantitySamplesRange {
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final FixedDecimal start;
|
||||
public final DecimalQuantity start;
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final FixedDecimal end;
|
||||
public final DecimalQuantity end;
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public FixedDecimalRange(FixedDecimal start, FixedDecimal end) {
|
||||
if (start.visibleDecimalDigitCount != end.visibleDecimalDigitCount) {
|
||||
public DecimalQuantitySamplesRange(DecimalQuantity start, DecimalQuantity end) {
|
||||
if (start.getPluralOperand(Operand.v)!= end.getPluralOperand(Operand.v)) {
|
||||
throw new IllegalArgumentException("Ranges must have the same number of visible decimals: " + start + "~" + end);
|
||||
}
|
||||
this.start = start;
|
||||
|
@ -1178,17 +1133,18 @@ public class PluralRules implements Serializable {
|
|||
@Deprecated
|
||||
@Override
|
||||
public String toString() {
|
||||
return start + (end == start ? "" : "~" + end);
|
||||
return start.toExponentString() + (end == start ? "" : "~" + end.toExponentString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of NumberInfo that includes all values with the same visibleFractionDigitCount.
|
||||
* A list of DecimalQuantity representing PluralRules that includes all
|
||||
* values with the same visibleFractionDigitCount.
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public static class FixedDecimalSamples {
|
||||
public static class DecimalQuantitySamples {
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
|
@ -1200,7 +1156,7 @@ public class PluralRules implements Serializable {
|
|||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final Set<FixedDecimalRange> samples;
|
||||
public final Set<DecimalQuantitySamplesRange> samples;
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
|
@ -1212,7 +1168,7 @@ public class PluralRules implements Serializable {
|
|||
* @param sampleType
|
||||
* @param samples
|
||||
*/
|
||||
private FixedDecimalSamples(SampleType sampleType, Set<FixedDecimalRange> samples, boolean bounded) {
|
||||
private DecimalQuantitySamples(SampleType sampleType, Set<DecimalQuantitySamplesRange> samples, boolean bounded) {
|
||||
super();
|
||||
this.sampleType = sampleType;
|
||||
this.samples = samples;
|
||||
|
@ -1221,11 +1177,11 @@ public class PluralRules implements Serializable {
|
|||
/*
|
||||
* Parse a list of the form described in CLDR. The source must be trimmed.
|
||||
*/
|
||||
static FixedDecimalSamples parse(String source) {
|
||||
static DecimalQuantitySamples parse(String source) {
|
||||
SampleType sampleType2;
|
||||
boolean bounded2 = true;
|
||||
boolean haveBound = false;
|
||||
Set<FixedDecimalRange> samples2 = new LinkedHashSet<>();
|
||||
Set<DecimalQuantitySamplesRange> samples2 = new LinkedHashSet<>();
|
||||
|
||||
if (source.startsWith("integer")) {
|
||||
sampleType2 = SampleType.INTEGER;
|
||||
|
@ -1248,25 +1204,33 @@ public class PluralRules implements Serializable {
|
|||
String[] rangeParts = TILDE_SEPARATED.split(range, 0);
|
||||
switch (rangeParts.length) {
|
||||
case 1:
|
||||
FixedDecimal sample = new FixedDecimal(rangeParts[0]);
|
||||
DecimalQuantity sample =
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString(rangeParts[0]);
|
||||
checkDecimal(sampleType2, sample);
|
||||
samples2.add(new FixedDecimalRange(sample, sample));
|
||||
samples2.add(new DecimalQuantitySamplesRange(sample, sample));
|
||||
break;
|
||||
case 2:
|
||||
FixedDecimal start = new FixedDecimal(rangeParts[0]);
|
||||
FixedDecimal end = new FixedDecimal(rangeParts[1]);
|
||||
DecimalQuantity start =
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString(rangeParts[0]);
|
||||
DecimalQuantity end =
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString(rangeParts[1]);
|
||||
checkDecimal(sampleType2, start);
|
||||
checkDecimal(sampleType2, end);
|
||||
samples2.add(new FixedDecimalRange(start, end));
|
||||
samples2.add(new DecimalQuantitySamplesRange(start, end));
|
||||
break;
|
||||
default: throw new IllegalArgumentException("Ill-formed number range: " + range);
|
||||
}
|
||||
}
|
||||
return new FixedDecimalSamples(sampleType2, Collections.unmodifiableSet(samples2), bounded2);
|
||||
return new DecimalQuantitySamples(sampleType2, Collections.unmodifiableSet(samples2), bounded2);
|
||||
}
|
||||
|
||||
private static void checkDecimal(SampleType sampleType2, FixedDecimal sample) {
|
||||
if ((sampleType2 == SampleType.INTEGER) != (sample.getVisibleDecimalDigitCount() == 0)) {
|
||||
private static void checkDecimal(SampleType sampleType2, DecimalQuantity sample) {
|
||||
// TODO(CLDR-15452): Remove the need for the fallback check for exponent notation integers classified
|
||||
// as "@decimal" type samples, if/when changes are made to
|
||||
// resolve https://unicode-org.atlassian.net/browse/CLDR-15452
|
||||
if ((sampleType2 == SampleType.INTEGER && sample.getPluralOperand(Operand.v) != 0)
|
||||
|| (sampleType2 == SampleType.DECIMAL && sample.getPluralOperand(Operand.v) == 0
|
||||
&& sample.getPluralOperand(Operand.e) == 0)) {
|
||||
throw new IllegalArgumentException("Ill-formed number range: " + sample);
|
||||
}
|
||||
}
|
||||
|
@ -1276,17 +1240,64 @@ public class PluralRules implements Serializable {
|
|||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Double> addSamples(Set<Double> result) {
|
||||
for (FixedDecimalRange item : samples) {
|
||||
// we have to convert to longs so we don't get strange double issues
|
||||
long startDouble = item.start.getShiftedValue();
|
||||
long endDouble = item.end.getShiftedValue();
|
||||
public Collection<Double> addSamples(Collection<Double> result) {
|
||||
addSamples(result, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
for (long d = startDouble; d <= endDouble; d += 1) {
|
||||
result.add(d/(double)item.start.baseFactor);
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public Collection<DecimalQuantity> addDecimalQuantitySamples(Collection<DecimalQuantity> result) {
|
||||
addSamples(null, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal CLDR
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public void addSamples(Collection<Double> doubleResult, Collection<DecimalQuantity> dqResult) {
|
||||
if ((doubleResult == null && dqResult == null)
|
||||
|| (doubleResult != null && dqResult != null)) {
|
||||
return;
|
||||
}
|
||||
boolean isDouble = doubleResult != null;
|
||||
for (DecimalQuantitySamplesRange range : samples) {
|
||||
DecimalQuantity start = range.start;
|
||||
DecimalQuantity end = range.end;
|
||||
int lowerDispMag = start.getLowerDisplayMagnitude();
|
||||
int exponent = start.getExponent();
|
||||
int incrementScale = lowerDispMag + exponent;
|
||||
BigDecimal incrementBd = BigDecimal.ONE.movePointRight(incrementScale);
|
||||
|
||||
for (DecimalQuantity dq = start.createCopy(); dq.toDouble() <= end.toDouble(); ) {
|
||||
if (isDouble) {
|
||||
double dblValue = dq.toDouble();
|
||||
// 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.
|
||||
if (!(dblValue == Math.floor(dblValue)) && dq.getPluralOperand(Operand.v) > 0) {
|
||||
doubleResult.add(dblValue);
|
||||
}
|
||||
} else {
|
||||
dqResult.add(dq);
|
||||
}
|
||||
|
||||
// Increment dq for next iteration
|
||||
java.math.BigDecimal dqBd = dq.toBigDecimal();
|
||||
java.math.BigDecimal newDqBd = dqBd.add(incrementBd);
|
||||
dq = new DecimalQuantity_DualStorageBCD(newDqBd);
|
||||
dq.setMinFraction(-lowerDispMag);
|
||||
dq.adjustMagnitude(-exponent);
|
||||
dq.adjustExponent(exponent);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1298,7 +1309,7 @@ public class PluralRules implements Serializable {
|
|||
public String toString() {
|
||||
StringBuilder b = new StringBuilder("@").append(sampleType.toString().toLowerCase(Locale.ENGLISH));
|
||||
boolean first = true;
|
||||
for (FixedDecimalRange item : samples) {
|
||||
for (DecimalQuantitySamplesRange item : samples) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
|
@ -1317,7 +1328,7 @@ public class PluralRules implements Serializable {
|
|||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<FixedDecimalRange> getSamples() {
|
||||
public Set<DecimalQuantitySamplesRange> getSamples() {
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
@ -1326,10 +1337,10 @@ public class PluralRules implements Serializable {
|
|||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public void getStartEndSamples(Set<FixedDecimal> target) {
|
||||
for (FixedDecimalRange item : samples) {
|
||||
target.add(item.start);
|
||||
target.add(item.end);
|
||||
public void getStartEndSamples(Set<DecimalQuantity> target) {
|
||||
for (DecimalQuantitySamplesRange range : samples) {
|
||||
target.add(range.start);
|
||||
target.add(range.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1610,19 +1621,19 @@ public class PluralRules implements Serializable {
|
|||
description = description.substring(x+1).trim();
|
||||
String[] constraintOrSamples = AT_SEPARATED.split(description, 0);
|
||||
boolean sampleFailure = false;
|
||||
FixedDecimalSamples integerSamples = null, decimalSamples = null;
|
||||
DecimalQuantitySamples integerSamples = null, decimalSamples = null;
|
||||
switch (constraintOrSamples.length) {
|
||||
case 1: break;
|
||||
case 2:
|
||||
integerSamples = FixedDecimalSamples.parse(constraintOrSamples[1]);
|
||||
integerSamples = DecimalQuantitySamples.parse(constraintOrSamples[1]);
|
||||
if (integerSamples.sampleType == SampleType.DECIMAL) {
|
||||
decimalSamples = integerSamples;
|
||||
integerSamples = null;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
integerSamples = FixedDecimalSamples.parse(constraintOrSamples[1]);
|
||||
decimalSamples = FixedDecimalSamples.parse(constraintOrSamples[2]);
|
||||
integerSamples = DecimalQuantitySamples.parse(constraintOrSamples[1]);
|
||||
decimalSamples = DecimalQuantitySamples.parse(constraintOrSamples[2]);
|
||||
if (integerSamples.sampleType != SampleType.INTEGER || decimalSamples.sampleType != SampleType.DECIMAL) {
|
||||
throw new IllegalArgumentException("Must have @integer then @decimal in " + description);
|
||||
}
|
||||
|
@ -1857,10 +1868,10 @@ public class PluralRules implements Serializable {
|
|||
private static final long serialVersionUID = 1;
|
||||
private final String keyword;
|
||||
private final Constraint constraint;
|
||||
private final FixedDecimalSamples integerSamples;
|
||||
private final FixedDecimalSamples decimalSamples;
|
||||
private final DecimalQuantitySamples integerSamples;
|
||||
private final DecimalQuantitySamples decimalSamples;
|
||||
|
||||
public Rule(String keyword, Constraint constraint, FixedDecimalSamples integerSamples, FixedDecimalSamples decimalSamples) {
|
||||
public Rule(String keyword, Constraint constraint, DecimalQuantitySamples integerSamples, DecimalQuantitySamples decimalSamples) {
|
||||
this.keyword = keyword;
|
||||
this.constraint = constraint;
|
||||
this.integerSamples = integerSamples;
|
||||
|
@ -1972,7 +1983,7 @@ public class PluralRules implements Serializable {
|
|||
|
||||
public boolean isLimited(String keyword, SampleType sampleType) {
|
||||
if (hasExplicitBoundingInfo) {
|
||||
FixedDecimalSamples mySamples = getDecimalSamples(keyword, sampleType);
|
||||
DecimalQuantitySamples mySamples = getDecimalSamples(keyword, sampleType);
|
||||
return mySamples == null ? true : mySamples.bounded;
|
||||
}
|
||||
|
||||
|
@ -2024,7 +2035,7 @@ public class PluralRules implements Serializable {
|
|||
return false;
|
||||
}
|
||||
|
||||
public FixedDecimalSamples getDecimalSamples(String keyword, SampleType sampleType) {
|
||||
public DecimalQuantitySamples getDecimalSamples(String keyword, SampleType sampleType) {
|
||||
for (Rule rule : rules) {
|
||||
if (rule.getKeyword().equals(keyword)) {
|
||||
return sampleType == SampleType.INTEGER ? rule.integerSamples : rule.decimalSamples;
|
||||
|
@ -2285,11 +2296,29 @@ public class PluralRules implements Serializable {
|
|||
* @stable ICU 4.8
|
||||
*/
|
||||
public double getUniqueKeywordValue(String keyword) {
|
||||
Collection<Double> values = getAllKeywordValues(keyword);
|
||||
DecimalQuantity uniqValDq = getUniqueKeywordDecimalQuantityValue(keyword);
|
||||
if (uniqValDq.equals(NO_UNIQUE_VALUE_DECIMAL_QUANTITY)) {
|
||||
return NO_UNIQUE_VALUE;
|
||||
} else {
|
||||
return uniqValDq.toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique value that this keyword matches, or {@link #NO_UNIQUE_VALUE}
|
||||
* if the keyword matches multiple values or is not defined for this PluralRules.
|
||||
*
|
||||
* @param keyword the keyword to check for a unique value
|
||||
* @internal Visible For Testing
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public DecimalQuantity getUniqueKeywordDecimalQuantityValue(String keyword) {
|
||||
Collection<DecimalQuantity> values = getAllKeywordDecimalQuantityValues(keyword);
|
||||
if (values != null && values.size() == 1) {
|
||||
return values.iterator().next();
|
||||
}
|
||||
return NO_UNIQUE_VALUE;
|
||||
return NO_UNIQUE_VALUE_DECIMAL_QUANTITY;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2302,6 +2331,31 @@ public class PluralRules implements Serializable {
|
|||
* @stable ICU 4.8
|
||||
*/
|
||||
public Collection<Double> getAllKeywordValues(String keyword) {
|
||||
Collection<DecimalQuantity> samples = getAllKeywordDecimalQuantityValues(keyword);
|
||||
if (samples == null) {
|
||||
return null;
|
||||
} else {
|
||||
Collection<Double> result = new LinkedHashSet<>();
|
||||
for (DecimalQuantity dq : samples) {
|
||||
result.add(dq.toDouble());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the values that trigger this keyword, or null if the number of such
|
||||
* values is unlimited.
|
||||
*
|
||||
* @param keyword the keyword
|
||||
* @return the values that trigger this keyword, or null. The returned collection
|
||||
* is immutable. It will be empty if the keyword is not defined.
|
||||
*
|
||||
* @internal Visible For Testing
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public Collection<DecimalQuantity> getAllKeywordDecimalQuantityValues(String keyword) {
|
||||
return getAllKeywordValues(keyword, SampleType.INTEGER);
|
||||
}
|
||||
|
||||
|
@ -2318,12 +2372,11 @@ public class PluralRules implements Serializable {
|
|||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public Collection<Double> getAllKeywordValues(String keyword, SampleType type) {
|
||||
public Collection<DecimalQuantity> getAllKeywordValues(String keyword, SampleType type) {
|
||||
if (!isLimited(keyword, type)) {
|
||||
return null;
|
||||
}
|
||||
Collection<Double> samples = getSamples(keyword, type);
|
||||
return samples == null ? null : Collections.unmodifiableCollection(samples);
|
||||
return getDecimalQuantitySamples(keyword, type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2340,6 +2393,22 @@ public class PluralRules implements Serializable {
|
|||
return getSamples(keyword, SampleType.INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of integer values for which select() would return that keyword,
|
||||
* or null if the keyword is not defined. The returned collection is unmodifiable.
|
||||
* The returned list is not complete, and there might be additional values that
|
||||
* would return the keyword.
|
||||
*
|
||||
* @param keyword the keyword to test
|
||||
* @return a list of values matching the keyword.
|
||||
* @internal CLDR
|
||||
* @deprecated ICU internal only
|
||||
*/
|
||||
@Deprecated
|
||||
public Collection<DecimalQuantity> getDecimalQuantitySamples(String keyword) {
|
||||
return getDecimalQuantitySamples(keyword, SampleType.INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of values for which select() would return that keyword,
|
||||
* or null if the keyword is not defined.
|
||||
|
@ -2356,15 +2425,43 @@ public class PluralRules implements Serializable {
|
|||
*/
|
||||
@Deprecated
|
||||
public Collection<Double> getSamples(String keyword, SampleType sampleType) {
|
||||
Collection<DecimalQuantity> samples = getDecimalQuantitySamples(keyword, sampleType);
|
||||
if (samples == null) {
|
||||
return null;
|
||||
} else {
|
||||
Collection<Double> result = new LinkedHashSet<>();
|
||||
for (DecimalQuantity dq: samples) {
|
||||
result.add(dq.toDouble());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of values for which select() would return that keyword,
|
||||
* or null if the keyword is not defined.
|
||||
* The returned collection is unmodifiable.
|
||||
* The returned list is not complete, and there might be additional values that
|
||||
* would return the keyword. The keyword might be defined, and yet have an empty set of samples,
|
||||
* IF there are samples for the other sampleType.
|
||||
*
|
||||
* @param keyword the keyword to test
|
||||
* @param sampleType the type of samples requested, INTEGER or DECIMAL
|
||||
* @return a list of values matching the keyword.
|
||||
* @internal CLDR
|
||||
* @deprecated ICU internal only
|
||||
*/
|
||||
@Deprecated
|
||||
public Collection<DecimalQuantity> getDecimalQuantitySamples(String keyword, SampleType sampleType) {
|
||||
if (!keywords.contains(keyword)) {
|
||||
return null;
|
||||
}
|
||||
Set<Double> result = new TreeSet<>();
|
||||
Set<DecimalQuantity> result = new LinkedHashSet<>();
|
||||
|
||||
if (rules.hasExplicitBoundingInfo) {
|
||||
FixedDecimalSamples samples = rules.getDecimalSamples(keyword, sampleType);
|
||||
DecimalQuantitySamples samples = rules.getDecimalSamples(keyword, sampleType);
|
||||
return samples == null ? Collections.unmodifiableSet(result)
|
||||
: Collections.unmodifiableSet(samples.addSamples(result));
|
||||
: Collections.unmodifiableCollection(samples.addDecimalQuantitySamples(result));
|
||||
}
|
||||
|
||||
// hack in case the rule is created without explicit samples
|
||||
|
@ -2373,28 +2470,31 @@ public class PluralRules implements Serializable {
|
|||
switch (sampleType) {
|
||||
case INTEGER:
|
||||
for (int i = 0; i < 200; ++i) {
|
||||
if (!addSample(keyword, i, maxCount, result)) {
|
||||
if (!addSample(keyword, new DecimalQuantity_DualStorageBCD(i), maxCount, result)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
addSample(keyword, 1000000, maxCount, result); // hack for Welsh
|
||||
addSample(keyword, new DecimalQuantity_DualStorageBCD(1000000), maxCount, result); // hack for Welsh
|
||||
break;
|
||||
case DECIMAL:
|
||||
for (int i = 0; i < 2000; ++i) {
|
||||
if (!addSample(keyword, new FixedDecimal(i/10d, 1), maxCount, result)) {
|
||||
DecimalQuantity_DualStorageBCD nextSample = new DecimalQuantity_DualStorageBCD(i);
|
||||
nextSample.adjustMagnitude(-1);
|
||||
if (!addSample(keyword, nextSample, maxCount, result)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
addSample(keyword, new FixedDecimal(1000000d, 1), maxCount, result); // hack for Welsh
|
||||
addSample(keyword, DecimalQuantity_DualStorageBCD.fromExponentString("1000000.0"), maxCount, result); // hack for Welsh
|
||||
break;
|
||||
}
|
||||
|
||||
return result.size() == 0 ? null : Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
private boolean addSample(String keyword, Number sample, int maxCount, Set<Double> result) {
|
||||
String selectedKeyword = sample instanceof FixedDecimal ? select((FixedDecimal)sample) : select(sample.doubleValue());
|
||||
private boolean addSample(String keyword, DecimalQuantity sample, int maxCount, Set<DecimalQuantity> result) {
|
||||
String selectedKeyword = select(sample);
|
||||
if (selectedKeyword.equals(keyword)) {
|
||||
result.add(sample.doubleValue());
|
||||
result.add(sample);
|
||||
if (--maxCount < 0) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2416,7 +2516,7 @@ public class PluralRules implements Serializable {
|
|||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public FixedDecimalSamples getDecimalSamples(String keyword, SampleType sampleType) {
|
||||
public DecimalQuantitySamples getDecimalSamples(String keyword, SampleType sampleType) {
|
||||
return rules.getDecimalSamples(keyword, sampleType);
|
||||
}
|
||||
|
||||
|
@ -2525,14 +2625,14 @@ public class PluralRules implements Serializable {
|
|||
* the offset used, or 0.0d if not. Internally, the offset is subtracted from each explicit value before
|
||||
* checking against the keyword values.
|
||||
* @param explicits
|
||||
* a set of Doubles that are used explicitly (eg [=0], "[=1]"). May be empty or null.
|
||||
* a set of {@code DecimalQuantity}s that are used explicitly (eg [=0], "[=1]"). May be empty or null.
|
||||
* @param uniqueValue
|
||||
* If non null, set to the unique value.
|
||||
* @return the KeywordStatus
|
||||
* @draft ICU 50
|
||||
*/
|
||||
public KeywordStatus getKeywordStatus(String keyword, int offset, Set<Double> explicits,
|
||||
Output<Double> uniqueValue) {
|
||||
public KeywordStatus getKeywordStatus(String keyword, int offset, Set<DecimalQuantity> explicits,
|
||||
Output<DecimalQuantity> uniqueValue) {
|
||||
return getKeywordStatus(keyword, offset, explicits, uniqueValue, SampleType.INTEGER);
|
||||
}
|
||||
/**
|
||||
|
@ -2544,7 +2644,7 @@ public class PluralRules implements Serializable {
|
|||
* the offset used, or 0.0d if not. Internally, the offset is subtracted from each explicit value before
|
||||
* checking against the keyword values.
|
||||
* @param explicits
|
||||
* a set of Doubles that are used explicitly (eg [=0], "[=1]"). May be empty or null.
|
||||
* a set of {@code DecimalQuantity}s that are used explicitly (eg [=0], "[=1]"). May be empty or null.
|
||||
* @param sampleType
|
||||
* request KeywordStatus relative to INTEGER or DECIMAL values
|
||||
* @param uniqueValue
|
||||
|
@ -2554,8 +2654,8 @@ public class PluralRules implements Serializable {
|
|||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public KeywordStatus getKeywordStatus(String keyword, int offset, Set<Double> explicits,
|
||||
Output<Double> uniqueValue, SampleType sampleType) {
|
||||
public KeywordStatus getKeywordStatus(String keyword, int offset,
|
||||
Set<DecimalQuantity> explicits, Output<DecimalQuantity> uniqueValue, SampleType sampleType) {
|
||||
if (uniqueValue != null) {
|
||||
uniqueValue.value = null;
|
||||
}
|
||||
|
@ -2568,7 +2668,7 @@ public class PluralRules implements Serializable {
|
|||
return KeywordStatus.UNBOUNDED;
|
||||
}
|
||||
|
||||
Collection<Double> values = getSamples(keyword, sampleType);
|
||||
Collection<DecimalQuantity> values = getDecimalQuantitySamples(keyword, sampleType);
|
||||
|
||||
int originalSize = values.size();
|
||||
|
||||
|
@ -2590,9 +2690,12 @@ public class PluralRules implements Serializable {
|
|||
|
||||
// Compute if the quick test is insufficient.
|
||||
|
||||
HashSet<Double> subtractedSet = new HashSet<>(values);
|
||||
for (Double explicit : explicits) {
|
||||
subtractedSet.remove(explicit - offset);
|
||||
ArrayList<DecimalQuantity> subtractedSet = new ArrayList<>(values);
|
||||
for (DecimalQuantity explicit : explicits) {
|
||||
BigDecimal explicitBd = explicit.toBigDecimal();
|
||||
BigDecimal valToRemoveBd = explicitBd.subtract(new BigDecimal(offset));
|
||||
DecimalQuantity_DualStorageBCD valToRemove = new DecimalQuantity_DualStorageBCD(valToRemoveBd);
|
||||
subtractedSet.remove(valToRemove);
|
||||
}
|
||||
if (subtractedSet.size() == 0) {
|
||||
return KeywordStatus.SUPPRESSED;
|
||||
|
|
|
@ -1,332 +0,0 @@
|
|||
// © 2016 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2013-2015, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.ibm.icu.text.PluralRules.FixedDecimal;
|
||||
import com.ibm.icu.text.PluralRules.KeywordStatus;
|
||||
import com.ibm.icu.util.Output;
|
||||
|
||||
/**
|
||||
* @author markdavis
|
||||
* Refactor samples as first step to moving into CLDR
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public class PluralSamples {
|
||||
|
||||
private PluralRules pluralRules;
|
||||
private final Map<String, List<Double>> _keySamplesMap;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final Map<String, Boolean> _keyLimitedMap;
|
||||
private final Map<String, Set<FixedDecimal>> _keyFractionSamplesMap;
|
||||
private final Set<FixedDecimal> _fractionSamples;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public PluralSamples(PluralRules pluralRules) {
|
||||
this.pluralRules = pluralRules;
|
||||
Set<String> keywords = pluralRules.getKeywords();
|
||||
// ensure both _keySamplesMap and _keyLimitedMap are initialized.
|
||||
// If this were allowed to vary on a per-call basis, we'd have to recheck and
|
||||
// possibly rebuild the samples cache. Doesn't seem worth it.
|
||||
// This 'max samples' value only applies to keywords that are unlimited, for
|
||||
// other keywords all the matching values are returned. This might be a lot.
|
||||
final int MAX_SAMPLES = 3;
|
||||
|
||||
Map<String, Boolean> temp = new HashMap<String, Boolean>();
|
||||
for (String k : keywords) {
|
||||
temp.put(k, pluralRules.isLimited(k));
|
||||
}
|
||||
_keyLimitedMap = temp;
|
||||
|
||||
Map<String, List<Double>> sampleMap = new HashMap<String, List<Double>>();
|
||||
int keywordsRemaining = keywords.size();
|
||||
|
||||
int limit = 128; // Math.max(5, getRepeatLimit() * MAX_SAMPLES) * 2;
|
||||
|
||||
for (int i = 0; keywordsRemaining > 0 && i < limit; ++i) {
|
||||
keywordsRemaining = addSimpleSamples(pluralRules, MAX_SAMPLES, sampleMap, keywordsRemaining, i / 2.0);
|
||||
}
|
||||
// Hack for Celtic
|
||||
keywordsRemaining = addSimpleSamples(pluralRules, MAX_SAMPLES, sampleMap, keywordsRemaining, 1000000);
|
||||
|
||||
|
||||
// collect explicit samples
|
||||
Map<String, Set<FixedDecimal>> sampleFractionMap = new HashMap<String, Set<FixedDecimal>>();
|
||||
Set<FixedDecimal> mentioned = new TreeSet<FixedDecimal>();
|
||||
// make sure that there is at least one 'other' value
|
||||
Map<String, Set<FixedDecimal>> foundKeywords = new HashMap<String, Set<FixedDecimal>>();
|
||||
for (FixedDecimal s : mentioned) {
|
||||
String keyword = pluralRules.select(s);
|
||||
addRelation(foundKeywords, keyword, s);
|
||||
}
|
||||
main:
|
||||
if (foundKeywords.size() != keywords.size()) {
|
||||
for (int i = 1; i < 1000; ++i) {
|
||||
boolean done = addIfNotPresent(i, mentioned, foundKeywords);
|
||||
if (done) break main;
|
||||
}
|
||||
// if we are not done, try tenths
|
||||
for (int i = 10; i < 1000; ++i) {
|
||||
boolean done = addIfNotPresent(i/10d, mentioned, foundKeywords);
|
||||
if (done) break main;
|
||||
}
|
||||
System.out.println("Failed to find sample for each keyword: " + foundKeywords + "\n\t" + pluralRules + "\n\t" + mentioned);
|
||||
}
|
||||
mentioned.add(new FixedDecimal(0)); // always there
|
||||
mentioned.add(new FixedDecimal(1)); // always there
|
||||
mentioned.add(new FixedDecimal(2)); // always there
|
||||
mentioned.add(new FixedDecimal(0.1,1)); // always there
|
||||
mentioned.add(new FixedDecimal(1.99,2)); // always there
|
||||
mentioned.addAll(fractions(mentioned));
|
||||
for (FixedDecimal s : mentioned) {
|
||||
String keyword = pluralRules.select(s);
|
||||
Set<FixedDecimal> list = sampleFractionMap.get(keyword);
|
||||
if (list == null) {
|
||||
list = new LinkedHashSet<FixedDecimal>(); // will be sorted because the iteration is
|
||||
sampleFractionMap.put(keyword, list);
|
||||
}
|
||||
list.add(s);
|
||||
}
|
||||
|
||||
if (keywordsRemaining > 0) {
|
||||
for (String k : keywords) {
|
||||
if (!sampleMap.containsKey(k)) {
|
||||
sampleMap.put(k, Collections.<Double>emptyList());
|
||||
}
|
||||
if (!sampleFractionMap.containsKey(k)) {
|
||||
sampleFractionMap.put(k, Collections.<FixedDecimal>emptySet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make lists immutable so we can return them directly
|
||||
for (Entry<String, List<Double>> entry : sampleMap.entrySet()) {
|
||||
sampleMap.put(entry.getKey(), Collections.unmodifiableList(entry.getValue()));
|
||||
}
|
||||
for (Entry<String, Set<FixedDecimal>> entry : sampleFractionMap.entrySet()) {
|
||||
sampleFractionMap.put(entry.getKey(), Collections.unmodifiableSet(entry.getValue()));
|
||||
}
|
||||
_keySamplesMap = sampleMap;
|
||||
_keyFractionSamplesMap = sampleFractionMap;
|
||||
_fractionSamples = Collections.unmodifiableSet(mentioned);
|
||||
}
|
||||
|
||||
private int addSimpleSamples(PluralRules pluralRules, final int MAX_SAMPLES, Map<String, List<Double>> sampleMap,
|
||||
int keywordsRemaining, double val) {
|
||||
String keyword = pluralRules.select(val);
|
||||
boolean keyIsLimited = _keyLimitedMap.get(keyword);
|
||||
|
||||
List<Double> list = sampleMap.get(keyword);
|
||||
if (list == null) {
|
||||
list = new ArrayList<Double>(MAX_SAMPLES);
|
||||
sampleMap.put(keyword, list);
|
||||
} else if (!keyIsLimited && list.size() == MAX_SAMPLES) {
|
||||
return keywordsRemaining;
|
||||
}
|
||||
list.add(Double.valueOf(val));
|
||||
|
||||
if (!keyIsLimited && list.size() == MAX_SAMPLES) {
|
||||
--keywordsRemaining;
|
||||
}
|
||||
return keywordsRemaining;
|
||||
}
|
||||
|
||||
private void addRelation(Map<String, Set<FixedDecimal>> foundKeywords, String keyword, FixedDecimal s) {
|
||||
Set<FixedDecimal> set = foundKeywords.get(keyword);
|
||||
if (set == null) {
|
||||
foundKeywords.put(keyword, set = new HashSet<FixedDecimal>());
|
||||
}
|
||||
set.add(s);
|
||||
}
|
||||
|
||||
private boolean addIfNotPresent(double d, Set<FixedDecimal> mentioned, Map<String, Set<FixedDecimal>> foundKeywords) {
|
||||
FixedDecimal numberInfo = new FixedDecimal(d);
|
||||
String keyword = pluralRules.select(numberInfo);
|
||||
if (!foundKeywords.containsKey(keyword) || keyword.equals("other")) {
|
||||
addRelation(foundKeywords, keyword, numberInfo);
|
||||
mentioned.add(numberInfo);
|
||||
if (keyword.equals("other")) {
|
||||
if (foundKeywords.get("other").size() > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final int[] TENS = {1, 10, 100, 1000, 10000, 100000, 1000000};
|
||||
|
||||
private static final int LIMIT_FRACTION_SAMPLES = 3;
|
||||
|
||||
|
||||
private Set<FixedDecimal> fractions(Set<FixedDecimal> original) {
|
||||
Set<FixedDecimal> toAddTo = new HashSet<FixedDecimal>();
|
||||
|
||||
Set<Integer> result = new HashSet<Integer>();
|
||||
for (FixedDecimal base1 : original) {
|
||||
result.add((int)base1.integerValue);
|
||||
}
|
||||
List<Integer> ints = new ArrayList<Integer>(result);
|
||||
Set<String> keywords = new HashSet<String>();
|
||||
|
||||
for (int j = 0; j < ints.size(); ++j) {
|
||||
Integer base = ints.get(j);
|
||||
String keyword = pluralRules.select(base);
|
||||
if (keywords.contains(keyword)) {
|
||||
continue;
|
||||
}
|
||||
keywords.add(keyword);
|
||||
toAddTo.add(new FixedDecimal(base,1)); // add .0
|
||||
toAddTo.add(new FixedDecimal(base,2)); // add .00
|
||||
Integer fract = getDifferentCategory(ints, keyword);
|
||||
if (fract >= TENS[LIMIT_FRACTION_SAMPLES-1]) { // make sure that we always get the value
|
||||
toAddTo.add(new FixedDecimal(base + "." + fract));
|
||||
} else {
|
||||
for (int visibleFractions = 1; visibleFractions < LIMIT_FRACTION_SAMPLES; ++visibleFractions) {
|
||||
for (int i = 1; i <= visibleFractions; ++i) {
|
||||
// with visible fractions = 3, and fract = 1, then we should get x.10, 0.01
|
||||
// with visible fractions = 3, and fract = 15, then we should get x.15, x.15
|
||||
if (fract >= TENS[i]) {
|
||||
continue;
|
||||
}
|
||||
toAddTo.add(new FixedDecimal(base + fract/(double)TENS[i], visibleFractions));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return toAddTo;
|
||||
}
|
||||
|
||||
private Integer getDifferentCategory(List<Integer> ints, String keyword) {
|
||||
for (int i = ints.size() - 1; i >= 0; --i) {
|
||||
Integer other = ints.get(i);
|
||||
String keywordOther = pluralRules.select(other);
|
||||
if (!keywordOther.equals(keyword)) {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
return 37;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public KeywordStatus getStatus(String keyword, int offset, Set<Double> explicits, Output<Double> uniqueValue) {
|
||||
if (uniqueValue != null) {
|
||||
uniqueValue.value = null;
|
||||
}
|
||||
|
||||
if (!pluralRules.getKeywords().contains(keyword)) {
|
||||
return KeywordStatus.INVALID;
|
||||
}
|
||||
Collection<Double> values = pluralRules.getAllKeywordValues(keyword);
|
||||
if (values == null) {
|
||||
return KeywordStatus.UNBOUNDED;
|
||||
}
|
||||
int originalSize = values.size();
|
||||
|
||||
if (explicits == null) {
|
||||
explicits = Collections.emptySet();
|
||||
}
|
||||
|
||||
// Quick check on whether there are multiple elements
|
||||
|
||||
if (originalSize > explicits.size()) {
|
||||
if (originalSize == 1) {
|
||||
if (uniqueValue != null) {
|
||||
uniqueValue.value = values.iterator().next();
|
||||
}
|
||||
return KeywordStatus.UNIQUE;
|
||||
}
|
||||
return KeywordStatus.BOUNDED;
|
||||
}
|
||||
|
||||
// Compute if the quick test is insufficient.
|
||||
|
||||
HashSet<Double> subtractedSet = new HashSet<Double>(values);
|
||||
for (Double explicit : explicits) {
|
||||
subtractedSet.remove(explicit - offset);
|
||||
}
|
||||
if (subtractedSet.size() == 0) {
|
||||
return KeywordStatus.SUPPRESSED;
|
||||
}
|
||||
|
||||
if (uniqueValue != null && subtractedSet.size() == 1) {
|
||||
uniqueValue.value = subtractedSet.iterator().next();
|
||||
}
|
||||
|
||||
return originalSize == 1 ? KeywordStatus.UNIQUE : KeywordStatus.BOUNDED;
|
||||
}
|
||||
|
||||
Map<String, List<Double>> getKeySamplesMap() {
|
||||
return _keySamplesMap;
|
||||
}
|
||||
|
||||
Map<String, Set<FixedDecimal>> getKeyFractionSamplesMap() {
|
||||
return _keyFractionSamplesMap;
|
||||
}
|
||||
|
||||
Set<FixedDecimal> getFractionSamples() {
|
||||
return _fractionSamples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the values that trigger this keyword, or null if the number of such
|
||||
* values is unlimited.
|
||||
*
|
||||
* @param keyword the keyword
|
||||
* @return the values that trigger this keyword, or null. The returned collection
|
||||
* is immutable. It will be empty if the keyword is not defined.
|
||||
* @stable ICU 4.8
|
||||
*/
|
||||
|
||||
Collection<Double> getAllKeywordValues(String keyword) {
|
||||
// HACK for now
|
||||
if (!pluralRules.getKeywords().contains(keyword)) {
|
||||
return Collections.<Double>emptyList();
|
||||
}
|
||||
Collection<Double> result = getKeySamplesMap().get(keyword);
|
||||
|
||||
// We depend on MAX_SAMPLES here. It's possible for a conjunction
|
||||
// of unlimited rules that 'looks' unlimited to return a limited
|
||||
// number of values. There's no bounds to this limited number, in
|
||||
// general, because you can construct arbitrarily complex rules. Since
|
||||
// we always generate 3 samples if a rule is really unlimited, that's
|
||||
// where we put the cutoff.
|
||||
if (result.size() > 2 && !_keyLimitedMap.get(keyword)) {
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import org.junit.runner.RunWith;
|
|||
import org.junit.runners.JUnit4;
|
||||
|
||||
import com.ibm.icu.dev.test.TestFmwk;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity;
|
||||
import com.ibm.icu.text.DecimalFormat;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.text.MessageFormat;
|
||||
|
@ -228,10 +229,10 @@ public class PluralFormatUnitTest extends TestFmwk {
|
|||
logln(localeName + "\ttoString\t" + rules.toString());
|
||||
Set<String> keywords = rules.getKeywords();
|
||||
for (String keyword : keywords) {
|
||||
Collection<Double> list = rules.getSamples(keyword);
|
||||
Collection<DecimalQuantity> list = rules.getDecimalQuantitySamples(keyword);
|
||||
if (list.size() == 0) {
|
||||
// if there aren't any integer samples, get the decimal ones.
|
||||
list = rules.getSamples(keyword, SampleType.DECIMAL);
|
||||
list = rules.getDecimalQuantitySamples(keyword, SampleType.DECIMAL);
|
||||
}
|
||||
|
||||
if (list == null || list.size() == 0) {
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Comparator;
|
|||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
@ -31,6 +32,8 @@ import java.util.Map.Entry;
|
|||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -41,6 +44,8 @@ import com.ibm.icu.dev.test.serializable.SerializableTestUtility;
|
|||
import com.ibm.icu.dev.util.CollectionUtilities;
|
||||
import com.ibm.icu.impl.Relation;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
|
||||
import com.ibm.icu.number.FormattedNumber;
|
||||
import com.ibm.icu.number.FormattedNumberRange;
|
||||
import com.ibm.icu.number.LocalizedNumberFormatter;
|
||||
|
@ -50,9 +55,9 @@ import com.ibm.icu.number.Precision;
|
|||
import com.ibm.icu.number.UnlocalizedNumberFormatter;
|
||||
import com.ibm.icu.text.NumberFormat;
|
||||
import com.ibm.icu.text.PluralRules;
|
||||
import com.ibm.icu.text.PluralRules.DecimalQuantitySamples;
|
||||
import com.ibm.icu.text.PluralRules.DecimalQuantitySamplesRange;
|
||||
import com.ibm.icu.text.PluralRules.FixedDecimal;
|
||||
import com.ibm.icu.text.PluralRules.FixedDecimalRange;
|
||||
import com.ibm.icu.text.PluralRules.FixedDecimalSamples;
|
||||
import com.ibm.icu.text.PluralRules.KeywordStatus;
|
||||
import com.ibm.icu.text.PluralRules.PluralType;
|
||||
import com.ibm.icu.text.PluralRules.SampleType;
|
||||
|
@ -177,17 +182,27 @@ public class PluralRulesTest extends TestFmwk {
|
|||
PluralRules test = PluralRules.createRules(description);
|
||||
|
||||
checkNewSamples(description, test, "one", PluralRules.SampleType.INTEGER, "@integer 3, 19", true,
|
||||
new FixedDecimal(3));
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("3"));
|
||||
checkNewSamples(description, test, "one", PluralRules.SampleType.DECIMAL, "@decimal 3.50~3.53, …", false,
|
||||
new FixedDecimal(3.5, 2));
|
||||
checkOldSamples(description, test, "one", SampleType.INTEGER, 3d, 19d);
|
||||
checkOldSamples(description, test, "one", SampleType.DECIMAL, 3.5d, 3.51d, 3.52d, 3.53d);
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("3.50"));
|
||||
checkOldSamples(description, test, "one", SampleType.INTEGER,
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("3"),
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("19"));
|
||||
checkOldSamples(description, test, "one", SampleType.DECIMAL,
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("3.50"),
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("3.51"),
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("3.52"),
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("3.53"));
|
||||
|
||||
checkNewSamples(description, test, "other", PluralRules.SampleType.INTEGER, "", true, null);
|
||||
checkNewSamples(description, test, "other", PluralRules.SampleType.DECIMAL, "@decimal 99.0~99.2, 999.0, …",
|
||||
false, new FixedDecimal(99d, 1));
|
||||
false, DecimalQuantity_DualStorageBCD.fromExponentString("99.0"));
|
||||
checkOldSamples(description, test, "other", SampleType.INTEGER);
|
||||
checkOldSamples(description, test, "other", SampleType.DECIMAL, 99d, 99.1, 99.2d, 999d);
|
||||
checkOldSamples(description, test, "other", SampleType.DECIMAL,
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("99.0"),
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("99.1"),
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("99.2"),
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("999.0"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,22 +220,26 @@ public class PluralRulesTest extends TestFmwk {
|
|||
// Creating the PluralRules object means being able to parse numbers
|
||||
// like 1e5 and 1.1e5
|
||||
PluralRules test = PluralRules.createRules(description);
|
||||
|
||||
// Currently, 'c' is the canonical representation of numbers with suppressed exponent, and 'e'
|
||||
// is an alias. The test helpers here skip 'e' for round-trip sample string parsing and formatting.
|
||||
|
||||
checkNewSamples(description, test, "one", PluralRules.SampleType.INTEGER, "@integer 0, 1, 1e5", true,
|
||||
new FixedDecimal(0));
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("0"));
|
||||
checkNewSamples(description, test, "one", PluralRules.SampleType.DECIMAL, "@decimal 0.0~1.5, 1.1e5", true,
|
||||
new FixedDecimal(0, 1));
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("0.0"));
|
||||
checkNewSamples(description, test, "many", PluralRules.SampleType.INTEGER, "@integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, …", false,
|
||||
new FixedDecimal(1000000));
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("1000000"));
|
||||
checkNewSamples(description, test, "many", PluralRules.SampleType.DECIMAL, "@decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …", false,
|
||||
FixedDecimal.createWithExponent(2.1, 1, 6));
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("2.1c6"));
|
||||
checkNewSamples(description, test, "other", PluralRules.SampleType.INTEGER, "@integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …", false,
|
||||
new FixedDecimal(2));
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("2"));
|
||||
checkNewSamples(description, test, "other", PluralRules.SampleType.DECIMAL, "@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …", false,
|
||||
new FixedDecimal(2.0, 1));
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("2.0"));
|
||||
}
|
||||
|
||||
/**
|
||||
* This test is for the support of X.YcZ compactnotation of numbers in
|
||||
* This test is for the support of X.YcZ compact notation of numbers in
|
||||
* the plural sample string.
|
||||
*/
|
||||
@Test
|
||||
|
@ -236,34 +255,53 @@ public class PluralRulesTest extends TestFmwk {
|
|||
// Note: Since `c` is currently an alias to `e`, the toString() of
|
||||
// FixedDecimal will return "1e5" even when input is "1c5".
|
||||
PluralRules test = PluralRules.createRules(description);
|
||||
checkNewSamples(description, test, "one", PluralRules.SampleType.INTEGER, "@integer 0, 1, 1e5", true,
|
||||
new FixedDecimal(0));
|
||||
checkNewSamples(description, test, "one", PluralRules.SampleType.DECIMAL, "@decimal 0.0~1.5, 1.1e5", true,
|
||||
new FixedDecimal(0, 1));
|
||||
checkNewSamples(description, test, "many", PluralRules.SampleType.INTEGER, "@integer 1000000, 2e6, 3e6, 4e6, 5e6, 6e6, 7e6, …", false,
|
||||
new FixedDecimal(1000000));
|
||||
checkNewSamples(description, test, "many", PluralRules.SampleType.DECIMAL, "@decimal 2.1e6, 3.1e6, 4.1e6, 5.1e6, 6.1e6, 7.1e6, …", false,
|
||||
FixedDecimal.createWithExponent(2.1, 1, 6));
|
||||
checkNewSamples(description, test, "other", PluralRules.SampleType.INTEGER, "@integer 2~17, 100, 1000, 10000, 100000, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, …", false,
|
||||
new FixedDecimal(2));
|
||||
checkNewSamples(description, test, "other", PluralRules.SampleType.DECIMAL, "@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1e5, 3.1e5, 4.1e5, 5.1e5, 6.1e5, 7.1e5, …", false,
|
||||
new FixedDecimal(2.0, 1));
|
||||
|
||||
checkNewSamples(description, test, "one", PluralRules.SampleType.INTEGER, "@integer 0, 1, 1c5", true,
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("0"));
|
||||
checkNewSamples(description, test, "one", PluralRules.SampleType.DECIMAL, "@decimal 0.0~1.5, 1.1c5", true,
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("0.0"));
|
||||
checkNewSamples(description, test, "many", PluralRules.SampleType.INTEGER, "@integer 1000000, 2c6, 3c6, 4c6, 5c6, 6c6, 7c6, …", false,
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("1000000"));
|
||||
checkNewSamples(description, test, "many", PluralRules.SampleType.DECIMAL, "@decimal 2.1c6, 3.1c6, 4.1c6, 5.1c6, 6.1c6, 7.1c6, …", false,
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("2.1c6"));
|
||||
checkNewSamples(description, test, "other", PluralRules.SampleType.INTEGER, "@integer 2~17, 100, 1000, 10000, 100000, 2c5, 3c5, 4c5, 5c5, 6c5, 7c5, …", false,
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("2"));
|
||||
checkNewSamples(description, test, "other", PluralRules.SampleType.DECIMAL, "@decimal 2.0~3.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 2.1c5, 3.1c5, 4.1c5, 5.1c5, 6.1c5, 7.1c5, …", false,
|
||||
DecimalQuantity_DualStorageBCD.fromExponentString("2.0"));
|
||||
}
|
||||
|
||||
public void checkOldSamples(String description, PluralRules rules, String keyword, SampleType sampleType,
|
||||
Double... expected) {
|
||||
Collection<Double> oldSamples = rules.getSamples(keyword, sampleType);
|
||||
if (!assertEquals("getOldSamples; " + keyword + "; " + description, new HashSet(Arrays.asList(expected)),
|
||||
oldSamples)) {
|
||||
DecimalQuantity... expected) {
|
||||
Collection<DecimalQuantity> oldSamples = rules.getDecimalQuantitySamples(keyword, sampleType);
|
||||
|
||||
// Collect actual (oldSamples) and expected (expectedSamplesList) into the
|
||||
// same concrete collection for comparison purposes.
|
||||
ArrayList<DecimalQuantity> oldSamplesList = new ArrayList(oldSamples);
|
||||
ArrayList<DecimalQuantity> expectedSamplesList = new ArrayList(Arrays.asList(expected));
|
||||
|
||||
if (!assertEquals("getOldSamples; " + keyword + "; " + description, expectedSamplesList,
|
||||
oldSamplesList)) {
|
||||
rules.getSamples(keyword, sampleType);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkNewSamples(String description, PluralRules test, String keyword, SampleType sampleType,
|
||||
String samplesString, boolean isBounded, FixedDecimal firstInRange) {
|
||||
String samplesString, boolean isBounded, DecimalQuantity firstInRange) {
|
||||
String title = description + ", " + sampleType;
|
||||
FixedDecimalSamples samples = test.getDecimalSamples(keyword, sampleType);
|
||||
DecimalQuantitySamples samples = test.getDecimalSamples(keyword, sampleType);
|
||||
if (samples != null) {
|
||||
|
||||
// For now, skip round-trip formatting test when samples string uses
|
||||
// 'e' instead of 'c' for compact notation.
|
||||
|
||||
// We are skipping tests for 'e' by replacing 'e' with 'c' in exponent
|
||||
// notation.
|
||||
Pattern p = Pattern.compile("(\\d+)(e)([-]?\\d+)");
|
||||
Matcher m = p.matcher(samplesString);
|
||||
if (m.find()) {
|
||||
samplesString = m.replaceAll("$1c$3");
|
||||
}
|
||||
|
||||
assertEquals("samples; " + title, samplesString, samples.toString());
|
||||
assertEquals("bounded; " + title, isBounded, samples.bounded);
|
||||
assertEquals("first; " + title, firstInRange, samples.samples.iterator().next().start);
|
||||
|
@ -391,11 +429,11 @@ public class PluralRulesTest extends TestFmwk {
|
|||
main: for (ULocale locale : factory.getAvailableULocales()) {
|
||||
PluralRules rules = factory.forLocale(locale);
|
||||
Map<String, PluralRules> keywordToRule = new HashMap<>();
|
||||
Collection<FixedDecimalSamples> samples = new LinkedHashSet<>();
|
||||
Collection<DecimalQuantitySamples> samples = new LinkedHashSet<>();
|
||||
|
||||
for (String keyword : rules.getKeywords()) {
|
||||
for (SampleType sampleType : SampleType.values()) {
|
||||
FixedDecimalSamples samples2 = rules.getDecimalSamples(keyword, sampleType);
|
||||
DecimalQuantitySamples samples2 = rules.getDecimalSamples(keyword, sampleType);
|
||||
if (samples2 != null) {
|
||||
samples.add(samples2);
|
||||
}
|
||||
|
@ -412,15 +450,16 @@ public class PluralRulesTest extends TestFmwk {
|
|||
}
|
||||
keywordToRule.put(keyword, singleRule);
|
||||
}
|
||||
Map<FixedDecimal, String> collisionTest = new TreeMap();
|
||||
for (FixedDecimalSamples sample3 : samples) {
|
||||
Set<FixedDecimalRange> samples2 = sample3.getSamples();
|
||||
|
||||
Map<DecimalQuantity, String> collisionTest = new LinkedHashMap();
|
||||
for (DecimalQuantitySamples sample3 : samples) {
|
||||
Set<DecimalQuantitySamplesRange> samples2 = sample3.getSamples();
|
||||
if (samples2 == null) {
|
||||
continue;
|
||||
}
|
||||
for (FixedDecimalRange sample : samples2) {
|
||||
for (DecimalQuantitySamplesRange sample : samples2) {
|
||||
for (int i = 0; i < 1; ++i) {
|
||||
FixedDecimal item = i == 0 ? sample.start : sample.end;
|
||||
DecimalQuantity item = i == 0 ? sample.start : sample.end;
|
||||
collisionTest.clear();
|
||||
for (Entry<String, PluralRules> entry : keywordToRule.entrySet()) {
|
||||
PluralRules rule = entry.getValue();
|
||||
|
@ -460,9 +499,9 @@ public class PluralRulesTest extends TestFmwk {
|
|||
}
|
||||
|
||||
public void checkValue(String title1, PluralRules rules, String expected, String value) {
|
||||
FixedDecimal fdNum = new FixedDecimal(value);
|
||||
DecimalQuantity dqNum = DecimalQuantity_DualStorageBCD.fromExponentString(value);
|
||||
|
||||
String result = rules.select(fdNum);
|
||||
String result = rules.select(dqNum);
|
||||
ULocale locale = null;
|
||||
assertEquals(getAssertMessage(title1, locale, rules, expected) + "; value: " + value, expected, result);
|
||||
}
|
||||
|
@ -733,13 +772,20 @@ public class PluralRulesTest extends TestFmwk {
|
|||
}
|
||||
}
|
||||
|
||||
private void assertRuleValue(String rule, double value) {
|
||||
private void assertRuleValue(String rule, DecimalQuantity value) {
|
||||
assertRuleKeyValue("a:" + rule, "a", value);
|
||||
}
|
||||
|
||||
private void assertRuleKeyValue(String rule, String key, double value) {
|
||||
private void assertRuleKeyValue(String rule, String key, DecimalQuantity value) {
|
||||
PluralRules pr = PluralRules.createRules(rule);
|
||||
assertEquals(rule, value, pr.getUniqueKeywordValue(key));
|
||||
|
||||
// as a DecimalQuantity
|
||||
assertEquals(rule, value, pr.getUniqueKeywordDecimalQuantityValue(key));
|
||||
|
||||
// as a double
|
||||
double expDouble = value.equals(PluralRules.NO_UNIQUE_VALUE_DECIMAL_QUANTITY) ?
|
||||
PluralRules.NO_UNIQUE_VALUE : value.toDouble();
|
||||
assertEquals(rule, expDouble, pr.getUniqueKeywordValue(key));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -747,26 +793,36 @@ public class PluralRulesTest extends TestFmwk {
|
|||
*/
|
||||
@Test
|
||||
public void TestGetUniqueKeywordValue() {
|
||||
assertRuleKeyValue("a: n is 1", "not_defined", PluralRules.NO_UNIQUE_VALUE); // key not defined
|
||||
assertRuleValue("n within 2..2", 2);
|
||||
assertRuleValue("n is 1", 1);
|
||||
assertRuleValue("n in 2..2", 2);
|
||||
assertRuleValue("n in 3..4", PluralRules.NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n within 3..4", PluralRules.NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 or n is 2", 2);
|
||||
assertRuleValue("n is 2 and n is 2", 2);
|
||||
assertRuleValue("n is 2 or n is 3", PluralRules.NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 and n is 3", PluralRules.NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 or n in 2..3", PluralRules.NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n is 2 and n in 2..3", 2);
|
||||
assertRuleKeyValue("a: n is 1", "other", PluralRules.NO_UNIQUE_VALUE); // key matches default rule
|
||||
assertRuleValue("n in 2,3", PluralRules.NO_UNIQUE_VALUE);
|
||||
assertRuleValue("n in 2,3..6 and n not in 2..3,5..6", 4);
|
||||
LocalizedNumberFormatter fmtr = NumberFormatter.withLocale(ULocale.ROOT);
|
||||
|
||||
assertRuleKeyValue("a: n is 1", "not_defined", PluralRules.NO_UNIQUE_VALUE_DECIMAL_QUANTITY); // key not defined
|
||||
assertRuleValue("n within 2..2", new DecimalQuantity_DualStorageBCD(2));
|
||||
assertRuleValue("n is 1", new DecimalQuantity_DualStorageBCD(1));
|
||||
assertRuleValue("n in 2..2", new DecimalQuantity_DualStorageBCD(2));
|
||||
assertRuleValue("n in 3..4", PluralRules.NO_UNIQUE_VALUE_DECIMAL_QUANTITY);
|
||||
assertRuleValue("n within 3..4", PluralRules.NO_UNIQUE_VALUE_DECIMAL_QUANTITY);
|
||||
assertRuleValue("n is 2 or n is 2", new DecimalQuantity_DualStorageBCD(2));
|
||||
assertRuleValue("n is 2 and n is 2", new DecimalQuantity_DualStorageBCD(2));
|
||||
assertRuleValue("n is 2 or n is 3", PluralRules.NO_UNIQUE_VALUE_DECIMAL_QUANTITY);
|
||||
assertRuleValue("n is 2 and n is 3", PluralRules.NO_UNIQUE_VALUE_DECIMAL_QUANTITY);
|
||||
assertRuleValue("n is 2 or n in 2..3", PluralRules.NO_UNIQUE_VALUE_DECIMAL_QUANTITY);
|
||||
assertRuleValue("n is 2 and n in 2..3", new DecimalQuantity_DualStorageBCD(2));
|
||||
assertRuleKeyValue("a: n is 1", "other", PluralRules.NO_UNIQUE_VALUE_DECIMAL_QUANTITY); // key matches default rule
|
||||
assertRuleValue("n in 2,3", PluralRules.NO_UNIQUE_VALUE_DECIMAL_QUANTITY);
|
||||
assertRuleValue("n in 2,3..6 and n not in 2..3,5..6", new DecimalQuantity_DualStorageBCD(4));
|
||||
}
|
||||
|
||||
/**
|
||||
* The version in PluralFormatUnitTest is not really a test, and it's in the wrong place anyway, so I'm putting a
|
||||
* variant of it here.
|
||||
*
|
||||
* Using the double API for getting plural samples, assert all samples match the keyword
|
||||
* they are listed under, for all locales.
|
||||
*
|
||||
* Specifically, iterate over all locales, get plural rules for the locale, iterate over every rule,
|
||||
* then iterate over every sample in the rule, parse sample to a number (double), use that number
|
||||
* as an input to .select() for the rules object, and assert the actual return plural keyword matches
|
||||
* what we expect based on the plural rule string.
|
||||
*/
|
||||
@Test
|
||||
public void TestGetSamples() {
|
||||
|
@ -775,10 +831,6 @@ public class PluralRulesTest extends TestFmwk {
|
|||
uniqueRuleSet.add(PluralRules.getFunctionalEquivalent(locale, null));
|
||||
}
|
||||
for (ULocale locale : uniqueRuleSet) {
|
||||
//if (locale.getLanguage().equals("fr") &&
|
||||
// logKnownIssue("21322", "PluralRules::getSamples cannot distinguish 1e5 from 100000")) {
|
||||
// continue;
|
||||
//}
|
||||
PluralRules rules = factory.forLocale(locale);
|
||||
logln("\nlocale: " + (locale == ULocale.ROOT ? "root" : locale.toString()) + ", rules: " + rules);
|
||||
Set<String> keywords = rules.getKeywords();
|
||||
|
@ -791,8 +843,8 @@ public class PluralRulesTest extends TestFmwk {
|
|||
if (list.size() == 0) {
|
||||
// when the samples (meaning integer samples) are null, then then integerSamples must be, and the
|
||||
// decimalSamples must not be
|
||||
FixedDecimalSamples integerSamples = rules.getDecimalSamples(keyword, SampleType.INTEGER);
|
||||
FixedDecimalSamples decimalSamples = rules.getDecimalSamples(keyword, SampleType.DECIMAL);
|
||||
DecimalQuantitySamples integerSamples = rules.getDecimalSamples(keyword, SampleType.INTEGER);
|
||||
DecimalQuantitySamples decimalSamples = rules.getDecimalSamples(keyword, SampleType.DECIMAL);
|
||||
assertTrue(getAssertMessage("List is not null", locale, rules, keyword), integerSamples == null
|
||||
&& decimalSamples != null && decimalSamples.samples.size() != 0);
|
||||
} else {
|
||||
|
@ -816,6 +868,131 @@ public class PluralRulesTest extends TestFmwk {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This replicates the setup of TestGetSamples(), but parses samples as DecimalQuantity instead of double.
|
||||
*
|
||||
* Using the DecimalQuantity API for getting plural samples, assert all samples match the keyword
|
||||
* they are listed under, for all locales.
|
||||
*
|
||||
* Specifically, iterate over all locales, get plural rules for the locale, iterate over every rule,
|
||||
* then iterate over every sample in the rule, parse sample to a number (DecimalQuantity), use that number
|
||||
* as an input to .select() for the rules object, and assert the actual return plural keyword matches
|
||||
* what we expect based on the plural rule string.
|
||||
*/
|
||||
@Test
|
||||
public void TestGetDecimalQuantitySamples() {
|
||||
Set<ULocale> uniqueRuleSet = new HashSet<>();
|
||||
for (ULocale locale : factory.getAvailableULocales()) {
|
||||
uniqueRuleSet.add(PluralRules.getFunctionalEquivalent(locale, null));
|
||||
}
|
||||
for (ULocale locale : uniqueRuleSet) {
|
||||
PluralRules rules = factory.forLocale(locale);
|
||||
logln("\nlocale: " + (locale == ULocale.ROOT ? "root" : locale.toString()) + ", rules: " + rules);
|
||||
Set<String> keywords = rules.getKeywords();
|
||||
for (String keyword : keywords) {
|
||||
Collection<DecimalQuantity> list = rules.getDecimalQuantitySamples(keyword);
|
||||
logln("keyword: " + keyword + ", samples: " + list);
|
||||
// with fractions, the samples can be empty and thus the list null. In that case, however, there will be
|
||||
// FixedDecimal values.
|
||||
// So patch the test for that.
|
||||
if (list.size() == 0) {
|
||||
// when the samples (meaning integer samples) are null, then then integerSamples must be, and the
|
||||
// decimalSamples must not be
|
||||
DecimalQuantitySamples integerSamples = rules.getDecimalSamples(keyword, SampleType.INTEGER);
|
||||
DecimalQuantitySamples decimalSamples = rules.getDecimalSamples(keyword, SampleType.DECIMAL);
|
||||
assertTrue(getAssertMessage("List is not null", locale, rules, keyword), integerSamples == null
|
||||
&& decimalSamples != null && decimalSamples.samples.size() != 0);
|
||||
} else {
|
||||
if (!assertTrue(getAssertMessage("Test getSamples.isEmpty", locale, rules, keyword),
|
||||
!list.isEmpty())) {
|
||||
rules.getDecimalQuantitySamples(keyword);
|
||||
}
|
||||
if (rules.toString().contains(": j")) {
|
||||
// hack until we remove j
|
||||
} else {
|
||||
for (DecimalQuantity value : list) {
|
||||
assertEquals(getAssertMessage("Match keyword", locale, rules, keyword) + "; value '"
|
||||
+ value + "'", keyword, rules.select(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertNull(locale + ", list is null", rules.getDecimalQuantitySamples("@#$%^&*"));
|
||||
assertNull(locale + ", list is null", rules.getDecimalQuantitySamples("@#$%^&*", SampleType.DECIMAL));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test addSamples (Java) / getSamplesFromString (C++) to ensure the expansion of plural rule sample range
|
||||
* expands to a sequence of sample numbers that is incremented as the right scale.
|
||||
*
|
||||
* Do this for numbers with fractional digits but no exponent.
|
||||
*/
|
||||
@Test
|
||||
public void testGetOrAddSamplesFromString() {
|
||||
PluralRules rules = PluralRules.createRules("testkeyword: e != 0 @decimal 2.0~4.0, …");
|
||||
|
||||
Set<String> keywords = rules.getKeywords();
|
||||
assertTrue("At least parse the test keyword in the test rule string", 0 < keywords.size());
|
||||
|
||||
String expKeyword = "testkeyword";
|
||||
Collection<DecimalQuantity> list = rules.getDecimalQuantitySamples(expKeyword, SampleType.DECIMAL);
|
||||
|
||||
String[] expDqStrs = {
|
||||
"2.0", "2.1", "2.2", "2.3", "2.4", "2.5", "2.6", "2.7", "2.8", "2.9",
|
||||
"3.0", "3.1", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9",
|
||||
"4.0"
|
||||
};
|
||||
assertEquals("Number of parsed samples from test string incorrect", expDqStrs.length, list.size());
|
||||
ArrayList<DecimalQuantity> actSamples = new ArrayList<>(list);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
String expDqStr = expDqStrs[i];
|
||||
DecimalQuantity sample = actSamples.get(i);
|
||||
String sampleStr = sample.toExponentString();
|
||||
|
||||
assertEquals("Expansion of sample range to sequence of sample values should increment at the right scale",
|
||||
expDqStr, sampleStr);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test addSamples (Java) / getSamplesFromString (C++) to ensure the expansion of plural rule sample range
|
||||
* expands to a sequence of sample numbers that is incremented as the right scale.
|
||||
*
|
||||
* Do this for numbers written in a notation that has an exponent, for which the number is an
|
||||
* integer (also as defined in the UTS 35 spec for the plural operands) but whose representation
|
||||
* has fractional digits in the significand written before the exponent.
|
||||
*/
|
||||
@Test
|
||||
public void testGetOrAddSamplesFromStringCompactNotation() {
|
||||
PluralRules rules = PluralRules.createRules("testkeyword: e != 0 @decimal 2.0c6~4.0c6, …");
|
||||
|
||||
Set<String> keywords = rules.getKeywords();
|
||||
assertTrue("At least parse the test keyword in the test rule string", 0 < keywords.size());
|
||||
|
||||
String expKeyword = "testkeyword";
|
||||
Collection<DecimalQuantity> list = rules.getDecimalQuantitySamples(expKeyword, SampleType.DECIMAL);
|
||||
|
||||
String[] expDqStrs = {
|
||||
"2.0c6", "2.1c6", "2.2c6", "2.3c6", "2.4c6", "2.5c6", "2.6c6", "2.7c6", "2.8c6", "2.9c6",
|
||||
"3.0c6", "3.1c6", "3.2c6", "3.3c6", "3.4c6", "3.5c6", "3.6c6", "3.7c6", "3.8c6", "3.9c6",
|
||||
"4.0c6"
|
||||
};
|
||||
assertEquals("Number of parsed samples from test string incorrect", expDqStrs.length, list.size());
|
||||
ArrayList<DecimalQuantity> actSamples = new ArrayList<>(list);
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
String expDqStr = expDqStrs[i];
|
||||
DecimalQuantity sample = actSamples.get(i);
|
||||
String sampleStr = sample.toExponentString();
|
||||
|
||||
assertEquals("Expansion of sample range to sequence of sample values should increment at the right scale",
|
||||
expDqStr, sampleStr);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public String getAssertMessage(String message, ULocale locale, PluralRules rules, String keyword) {
|
||||
String ruleString = "";
|
||||
if (keyword != null) {
|
||||
|
@ -882,24 +1059,42 @@ public class PluralRulesTest extends TestFmwk {
|
|||
if (valueList != null) {
|
||||
valueList = valueList.trim();
|
||||
}
|
||||
Collection<Double> values;
|
||||
Collection<DecimalQuantity> values;
|
||||
if (valueList == null || valueList.length() == 0) {
|
||||
values = Collections.EMPTY_SET;
|
||||
} else if ("null".equals(valueList)) {
|
||||
values = null;
|
||||
} else {
|
||||
values = new TreeSet<>();
|
||||
values = new LinkedHashSet<>();
|
||||
for (String value : valueList.split(",")) {
|
||||
values.add(Double.parseDouble(value));
|
||||
values.add(DecimalQuantity_DualStorageBCD.fromExponentString(value));
|
||||
}
|
||||
}
|
||||
|
||||
Collection<Double> results = p.getAllKeywordValues(keyword);
|
||||
assertEquals(keyword + " in " + ruleDescription, values, results == null ? null : new HashSet(results));
|
||||
Collection<DecimalQuantity> results = p.getAllKeywordDecimalQuantityValues(keyword);
|
||||
|
||||
// Convert DecimalQuantity using a 1:1 conversion to String for comparison purposes
|
||||
Set<String> valuesForComparison = new HashSet<>();
|
||||
if (values != null) {
|
||||
for (DecimalQuantity dq : values) {
|
||||
valuesForComparison.add(dq.toExponentString());
|
||||
}
|
||||
}
|
||||
Set<String> resultsForComparison = new HashSet<>();
|
||||
if (results != null) {
|
||||
for (DecimalQuantity dq : results) {
|
||||
resultsForComparison.add(dq.toExponentString());
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(keyword + " in " + ruleDescription,
|
||||
values == null ? null : valuesForComparison,
|
||||
results == null ? null : resultsForComparison
|
||||
);
|
||||
|
||||
if (results != null) {
|
||||
try {
|
||||
results.add(PluralRules.NO_UNIQUE_VALUE);
|
||||
results.add(PluralRules.NO_UNIQUE_VALUE_DECIMAL_QUANTITY);
|
||||
fail("returned set is modifiable");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// pass
|
||||
|
@ -967,13 +1162,9 @@ public class PluralRulesTest extends TestFmwk {
|
|||
for (String keyword : rules.getKeywords()) {
|
||||
boolean isLimited = rules.isLimited(keyword, sampleType);
|
||||
boolean computeLimited = rules.computeLimited(keyword, sampleType);
|
||||
if (!keyword.equals("other") && !(locale.getLanguage().equals("fr") && logKnownIssue("ICU-21322", "fr plurals many case computeLimited == isLimited"))) {
|
||||
assertEquals(getAssertMessage("computeLimited == isLimited", locale, rules, keyword),
|
||||
computeLimited, isLimited);
|
||||
}
|
||||
Collection<Double> samples = rules.getSamples(keyword, sampleType);
|
||||
Collection<DecimalQuantity> samples = rules.getDecimalQuantitySamples(keyword, sampleType);
|
||||
assertNotNull(getAssertMessage("Samples must not be null", locale, rules, keyword), samples);
|
||||
/* FixedDecimalSamples decimalSamples = */rules.getDecimalSamples(keyword, sampleType);
|
||||
rules.getDecimalSamples(keyword, sampleType);
|
||||
// assertNotNull(getAssertMessage("Decimal samples must be null if unlimited", locale, rules,
|
||||
// keyword), decimalSamples);
|
||||
}
|
||||
|
@ -985,29 +1176,30 @@ public class PluralRulesTest extends TestFmwk {
|
|||
@Test
|
||||
public void TestKeywords() {
|
||||
Set<String> possibleKeywords = new LinkedHashSet(Arrays.asList("zero", "one", "two", "few", "many", "other"));
|
||||
DecimalQuantity ONE_INTEGER = DecimalQuantity_DualStorageBCD.fromExponentString("1");
|
||||
Object[][][] tests = {
|
||||
// format is locale, explicits, then triples of keyword, status, unique value.
|
||||
{ { "en", null }, { "one", KeywordStatus.UNIQUE, 1.0d }, { "other", KeywordStatus.UNBOUNDED, null } },
|
||||
{ { "pl", null }, { "one", KeywordStatus.UNIQUE, 1.0d }, { "few", KeywordStatus.UNBOUNDED, null },
|
||||
{ { "en", null }, { "one", KeywordStatus.UNIQUE, ONE_INTEGER }, { "other", KeywordStatus.UNBOUNDED, null } },
|
||||
{ { "pl", null }, { "one", KeywordStatus.UNIQUE, ONE_INTEGER }, { "few", KeywordStatus.UNBOUNDED, null },
|
||||
{ "many", KeywordStatus.UNBOUNDED, null },
|
||||
{ "other", KeywordStatus.SUPPRESSED, null, KeywordStatus.UNBOUNDED, null } // note that it is
|
||||
// suppressed in
|
||||
// INTEGER but not
|
||||
// DECIMAL
|
||||
}, { { "en", new HashSet<>(Arrays.asList(1.0d)) }, // check that 1 is suppressed
|
||||
}, { { "en", new HashSet<>(Arrays.asList(ONE_INTEGER)) }, // check that 1 is suppressed
|
||||
{ "one", KeywordStatus.SUPPRESSED, null }, { "other", KeywordStatus.UNBOUNDED, null } }, };
|
||||
Output<Double> uniqueValue = new Output<>();
|
||||
Output<DecimalQuantity> uniqueValue = new Output<>();
|
||||
for (Object[][] test : tests) {
|
||||
ULocale locale = new ULocale((String) test[0][0]);
|
||||
// NumberType numberType = (NumberType) test[1];
|
||||
Set<Double> explicits = (Set<Double>) test[0][1];
|
||||
Set<DecimalQuantity> explicits = (Set<DecimalQuantity>) test[0][1];
|
||||
PluralRules pluralRules = factory.forLocale(locale);
|
||||
LinkedHashSet<String> remaining = new LinkedHashSet(possibleKeywords);
|
||||
for (int i = 1; i < test.length; ++i) {
|
||||
Object[] row = test[i];
|
||||
String keyword = (String) row[0];
|
||||
KeywordStatus statusExpected = (KeywordStatus) row[1];
|
||||
Double uniqueExpected = (Double) row[2];
|
||||
DecimalQuantity uniqueExpected = (DecimalQuantity) row[2];
|
||||
remaining.remove(keyword);
|
||||
KeywordStatus status = pluralRules.getKeywordStatus(keyword, 0, explicits, uniqueValue);
|
||||
assertEquals(getAssertMessage("Unique Value", locale, pluralRules, keyword), uniqueExpected,
|
||||
|
@ -1015,7 +1207,7 @@ public class PluralRulesTest extends TestFmwk {
|
|||
assertEquals(getAssertMessage("Keyword Status", locale, pluralRules, keyword), statusExpected, status);
|
||||
if (row.length > 3) {
|
||||
statusExpected = (KeywordStatus) row[3];
|
||||
uniqueExpected = (Double) row[4];
|
||||
uniqueExpected = (DecimalQuantity) row[4];
|
||||
status = pluralRules.getKeywordStatus(keyword, 0, explicits, uniqueValue, SampleType.DECIMAL);
|
||||
assertEquals(getAssertMessage("Unique Value - decimal", locale, pluralRules, keyword),
|
||||
uniqueExpected, uniqueValue.value);
|
||||
|
@ -1033,6 +1225,11 @@ public class PluralRulesTest extends TestFmwk {
|
|||
|
||||
// For the time being, the compact notation exponent operand `c` is an alias
|
||||
// for the scientific exponent operand `e` and compact notation.
|
||||
/**
|
||||
* Test the proper plural rule keyword selection given an input number that is
|
||||
* already formatted into scientific notation. This exercises the `e` plural operand
|
||||
* for the formatted number.
|
||||
*/
|
||||
@Test
|
||||
public void testScientificPluralKeyword() {
|
||||
PluralRules rules = PluralRules.createRules("one: i = 0,1 @integer 0, 1 @decimal 0.0~1.5; many: e = 0 and i % 1000000 = 0 and v = 0 or " +
|
||||
|
@ -1082,6 +1279,11 @@ public class PluralRulesTest extends TestFmwk {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the proper plural rule keyword selection given an input number that is
|
||||
* already formatted into compact notation. This exercises the `c` plural operand
|
||||
* for the formatted number.
|
||||
*/
|
||||
@Test
|
||||
public void testCompactDecimalPluralKeyword() {
|
||||
PluralRules rules = PluralRules.createRules("one: i = 0,1 @integer 0, 1 @decimal 0.0~1.5; many: c = 0 and i % 1000000 = 0 and v = 0 or " +
|
||||
|
@ -1261,8 +1463,13 @@ public class PluralRulesTest extends TestFmwk {
|
|||
|
||||
@Test
|
||||
public void TestLocales() {
|
||||
// This test will fail when the locale snapshot gets out of sync with the real CLDR data.
|
||||
// In that case, temporarily use "if (true)",
|
||||
// copy & paste the output into the initializer above,
|
||||
// and revert to "if (false)" for normal testing.
|
||||
if (false) {
|
||||
generateLOCALE_SNAPSHOT();
|
||||
return;
|
||||
}
|
||||
for (String test : LOCALE_SNAPSHOT) {
|
||||
test = test.trim();
|
||||
|
@ -1308,7 +1515,7 @@ public class PluralRulesTest extends TestFmwk {
|
|||
System.out.print(" \"" + CollectionUtilities.join(locales, ","));
|
||||
for (StandardPluralCategories spc : set) {
|
||||
String keyword = spc.toString();
|
||||
FixedDecimalSamples samples = rule.getDecimalSamples(keyword, SampleType.INTEGER);
|
||||
DecimalQuantitySamples samples = rule.getDecimalSamples(keyword, SampleType.INTEGER);
|
||||
System.out.print("; " + spc + ": " + samples);
|
||||
}
|
||||
System.out.println("\",");
|
||||
|
|
Loading…
Add table
Reference in a new issue