ICU-13839 Adding FormattedNumber API to PluralRules.

- Makes new dependency class for PluralRules+FormattedNumber.
This commit is contained in:
Shane Carr 2019-01-10 23:42:43 -08:00 committed by Shane F. Carr
parent dcc1300613
commit 1f85e94068
18 changed files with 421 additions and 149 deletions

View file

@ -101,7 +101,7 @@ uregion.o reldatefmt.o quantityformatter.o measunit.o \
sharedbreakiterator.o scientificnumberformatter.o dayperiodrules.o nounit.o \
number_affixutils.o number_compact.o number_decimalquantity.o \
number_decimfmtprops.o number_fluent.o number_formatimpl.o number_grouping.o \
number_integerwidth.o number_longnames.o number_modifiers.o number_notation.o \
number_integerwidth.o number_longnames.o number_modifiers.o number_notation.o number_output.o \
number_padding.o number_patternmodifier.o number_patternstring.o \
number_rounding.o number_scientific.o number_stringbuilder.o number_utils.o number_asformat.o \
number_mapper.o number_multiplier.o number_currencysymbols.o number_skeletons.o number_capi.o \

View file

@ -270,6 +270,7 @@
<ClCompile Include="number_longnames.cpp" />
<ClCompile Include="number_modifiers.cpp" />
<ClCompile Include="number_notation.cpp" />
<ClCompile Include="number_output.cpp" />
<ClCompile Include="number_padding.cpp" />
<ClCompile Include="number_patternmodifier.cpp" />
<ClCompile Include="number_patternstring.cpp" />

View file

@ -561,6 +561,9 @@
<ClCompile Include="number_notation.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_output.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_padding.cpp">
<Filter>formatting</Filter>
</ClCompile>

View file

@ -377,6 +377,7 @@
<ClCompile Include="number_longnames.cpp" />
<ClCompile Include="number_modifiers.cpp" />
<ClCompile Include="number_notation.cpp" />
<ClCompile Include="number_output.cpp" />
<ClCompile Include="number_padding.cpp" />
<ClCompile Include="number_patternmodifier.cpp" />
<ClCompile Include="number_patternstring.cpp" />

View file

@ -61,6 +61,16 @@ UFormattedNumberImpl::~UFormattedNumberImpl() {
U_NAMESPACE_END
const DecimalQuantity* icu::number::impl::validateUFormattedNumberToDecimalQuantity(
const UFormattedNumber* uresult, UErrorCode& status) {
auto* result = UFormattedNumberApiHelper::validate(uresult, status);
if (U_FAILURE(status)) {
return nullptr;
}
return &result->fData.quantity;
}
U_CAPI UNumberFormatter* U_EXPORT2
unumf_openForSkeletonAndLocale(const UChar* skeleton, int32_t skeletonLen, const char* locale,

View file

@ -770,111 +770,4 @@ Format* LocalizedNumberFormatter::toFormat(UErrorCode& status) const {
}
FormattedNumber::FormattedNumber(FormattedNumber&& src) U_NOEXCEPT
: fResults(src.fResults), fErrorCode(src.fErrorCode) {
// Disown src.fResults to prevent double-deletion
src.fResults = nullptr;
src.fErrorCode = U_INVALID_STATE_ERROR;
}
FormattedNumber& FormattedNumber::operator=(FormattedNumber&& src) U_NOEXCEPT {
delete fResults;
fResults = src.fResults;
fErrorCode = src.fErrorCode;
// Disown src.fResults to prevent double-deletion
src.fResults = nullptr;
src.fErrorCode = U_INVALID_STATE_ERROR;
return *this;
}
UnicodeString FormattedNumber::toString(UErrorCode& status) const {
if (U_FAILURE(status)) {
return ICU_Utility::makeBogusString();
}
if (fResults == nullptr) {
status = fErrorCode;
return ICU_Utility::makeBogusString();
}
return fResults->string.toUnicodeString();
}
UnicodeString FormattedNumber::toTempString(UErrorCode& status) const {
if (U_FAILURE(status)) {
return ICU_Utility::makeBogusString();
}
if (fResults == nullptr) {
status = fErrorCode;
return ICU_Utility::makeBogusString();
}
return fResults->string.toTempUnicodeString();
}
Appendable& FormattedNumber::appendTo(Appendable& appendable, UErrorCode& status) const {
if (U_FAILURE(status)) {
return appendable;
}
if (fResults == nullptr) {
status = fErrorCode;
return appendable;
}
appendable.appendString(fResults->string.chars(), fResults->string.length());
return appendable;
}
UBool FormattedNumber::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const {
if (U_FAILURE(status)) {
return FALSE;
}
if (fResults == nullptr) {
status = fErrorCode;
return FALSE;
}
// NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
return fResults->string.nextPosition(cfpos, status) ? TRUE : FALSE;
}
UBool FormattedNumber::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
if (U_FAILURE(status)) {
return FALSE;
}
if (fResults == nullptr) {
status = fErrorCode;
return FALSE;
}
// NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
}
void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
FieldPositionIteratorHandler fpih(&iterator, status);
getAllFieldPositionsImpl(fpih, status);
}
void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih,
UErrorCode& status) const {
if (U_FAILURE(status)) {
return;
}
if (fResults == nullptr) {
status = fErrorCode;
return;
}
fResults->string.getAllFieldPositions(fpih, status);
}
void FormattedNumber::getDecimalQuantity(DecimalQuantity& output, UErrorCode& status) const {
if (U_FAILURE(status)) {
return;
}
if (fResults == nullptr) {
status = fErrorCode;
return;
}
output = fResults->quantity;
}
FormattedNumber::~FormattedNumber() {
delete fResults;
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -0,0 +1,128 @@
// © 2019 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/numberformatter.h"
#include "number_utypes.h"
#include "util.h"
#include "number_decimalquantity.h"
U_NAMESPACE_BEGIN
namespace number {
FormattedNumber::FormattedNumber(FormattedNumber&& src) U_NOEXCEPT
: fResults(src.fResults), fErrorCode(src.fErrorCode) {
// Disown src.fResults to prevent double-deletion
src.fResults = nullptr;
src.fErrorCode = U_INVALID_STATE_ERROR;
}
FormattedNumber& FormattedNumber::operator=(FormattedNumber&& src) U_NOEXCEPT {
delete fResults;
fResults = src.fResults;
fErrorCode = src.fErrorCode;
// Disown src.fResults to prevent double-deletion
src.fResults = nullptr;
src.fErrorCode = U_INVALID_STATE_ERROR;
return *this;
}
UnicodeString FormattedNumber::toString(UErrorCode& status) const {
if (U_FAILURE(status)) {
return ICU_Utility::makeBogusString();
}
if (fResults == nullptr) {
status = fErrorCode;
return ICU_Utility::makeBogusString();
}
return fResults->string.toUnicodeString();
}
UnicodeString FormattedNumber::toTempString(UErrorCode& status) const {
if (U_FAILURE(status)) {
return ICU_Utility::makeBogusString();
}
if (fResults == nullptr) {
status = fErrorCode;
return ICU_Utility::makeBogusString();
}
return fResults->string.toTempUnicodeString();
}
Appendable& FormattedNumber::appendTo(Appendable& appendable, UErrorCode& status) const {
if (U_FAILURE(status)) {
return appendable;
}
if (fResults == nullptr) {
status = fErrorCode;
return appendable;
}
appendable.appendString(fResults->string.chars(), fResults->string.length());
return appendable;
}
UBool FormattedNumber::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const {
if (U_FAILURE(status)) {
return FALSE;
}
if (fResults == nullptr) {
status = fErrorCode;
return FALSE;
}
// NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
return fResults->string.nextPosition(cfpos, status) ? TRUE : FALSE;
}
UBool FormattedNumber::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
if (U_FAILURE(status)) {
return FALSE;
}
if (fResults == nullptr) {
status = fErrorCode;
return FALSE;
}
// NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
}
void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
FieldPositionIteratorHandler fpih(&iterator, status);
getAllFieldPositionsImpl(fpih, status);
}
void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih,
UErrorCode& status) const {
if (U_FAILURE(status)) {
return;
}
if (fResults == nullptr) {
status = fErrorCode;
return;
}
fResults->string.getAllFieldPositions(fpih, status);
}
void FormattedNumber::getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const {
if (U_FAILURE(status)) {
return;
}
if (fResults == nullptr) {
status = fErrorCode;
return;
}
output = fResults->quantity;
}
FormattedNumber::~FormattedNumber() {
delete fResults;
}
} // namespace number
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -27,6 +27,11 @@ struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper {
};
/** Helper function used in upluralrules.cpp */
const DecimalQuantity* validateUFormattedNumberToDecimalQuantity(
const UFormattedNumber* uresult, UErrorCode& status);
/**
* Struct for data used by FormattedNumber.
*

View file

@ -35,6 +35,7 @@
#include "sharedpluralrules.h"
#include "unifiedcache.h"
#include "number_decimalquantity.h"
#include "util.h"
#if !UCONFIG_NO_FORMATTING
@ -264,6 +265,16 @@ PluralRules::select(double number) const {
return select(FixedDecimal(number));
}
UnicodeString
PluralRules::select(const number::FormattedNumber& number, UErrorCode& status) const {
DecimalQuantity dq;
number.getDecimalQuantity(dq, status);
if (U_FAILURE(status)) {
return ICU_Utility::makeBogusString();
}
return select(dq);
}
UnicodeString
PluralRules::select(const IFixedDecimal &number) const {
if (mRules == nullptr) {

View file

@ -50,6 +50,10 @@ class PluralKeywordEnumeration;
class AndConstraint;
class SharedPluralRules;
namespace number {
class FormattedNumber;
}
/**
* Defines rules for mapping non-negative numeric values onto a small set of
* keywords. Rules are constructed from a text description, consisting
@ -323,9 +327,9 @@ public:
#endif /* U_HIDE_INTERNAL_API */
/**
* Given a number, returns the keyword of the first rule that applies to
* the number. This function can be used with isKeyword* functions to
* determine the keyword for default plural rules.
* Given an integer, returns the keyword of the first rule
* that applies to the number. This function can be used with
* isKeyword* functions to determine the keyword for default plural rules.
*
* @param number The number for which the rule has to be determined.
* @return The keyword of the selected rule.
@ -334,9 +338,9 @@ public:
UnicodeString select(int32_t number) const;
/**
* Given a number, returns the keyword of the first rule that applies to
* the number. This function can be used with isKeyword* functions to
* determine the keyword for default plural rules.
* Given a floating-point number, returns the keyword of the first rule
* that applies to the number. This function can be used with
* isKeyword* functions to determine the keyword for default plural rules.
*
* @param number The number for which the rule has to be determined.
* @return The keyword of the selected rule.
@ -344,6 +348,23 @@ public:
*/
UnicodeString select(double number) const;
/**
* Given a formatted number, returns the keyword of the first rule
* that applies to the number. This function can be used with
* isKeyword* functions to determine the keyword for default plural rules.
*
* A FormattedNumber allows you to specify an exponent or trailing zeros,
* which can affect the plural category. To get a FormattedNumber, see
* NumberFormatter.
*
* @param number The number for which the rule has to be determined.
* @param status Set if an error occurs while selecting plural keyword.
* This could happen if the FormattedNumber is invalid.
* @return The keyword of the selected rule.
* @draft ICU 64
*/
UnicodeString select(const number::FormattedNumber& number, UErrorCode& status) const;
#ifndef U_HIDE_INTERNAL_API
/**
* @internal

View file

@ -20,6 +20,9 @@
#include "unicode/unum.h"
#endif /* U_HIDE_INTERNAL_API */
// Forward-declaration
struct UFormattedNumber;
/**
* \file
* \brief C API: Plural rules, select plural keywords for numeric values.
@ -132,14 +135,15 @@ U_NAMESPACE_END
/**
* Given a number, returns the keyword of the first rule that
* Given a floating-point number, returns the keyword of the first rule that
* applies to the number, according to the supplied UPluralRules object.
* @param uplrules The UPluralRules object specifying the rules.
* @param number The number for which the rule has to be determined.
* @param keyword The keyword of the rule that applies to number.
* @param capacity The capacity of keyword.
* @param keyword An output buffer to write the keyword of the rule that
* applies to number.
* @param capacity The capacity of the keyword buffer.
* @param status A pointer to a UErrorCode to receive any errors.
* @return The length of keyword.
* @return The length of the keyword.
* @stable ICU 4.8
*/
U_CAPI int32_t U_EXPORT2
@ -148,6 +152,29 @@ uplrules_select(const UPluralRules *uplrules,
UChar *keyword, int32_t capacity,
UErrorCode *status);
/**
* Given a formatted number, returns the keyword of the first rule
* that applies to the number, according to the supplied UPluralRules object.
*
* A UFormattedNumber allows you to specify an exponent or trailing zeros,
* which can affect the plural category. To get a UFormattedNumber, see
* {@link UNumberFormatter}.
*
* @param uplrules The UPluralRules object specifying the rules.
* @param number The formatted number for which the rule has to be determined.
* @param keyword The destination buffer for the keyword of the rule that
* applies to number.
* @param capacity The capacity of the keyword buffer.
* @param status A pointer to a UErrorCode to receive any errors.
* @return The length of the keyword.
* @draft ICU 64
*/
U_CAPI int32_t U_EXPORT2
uplrules_selectFormatted(const UPluralRules *uplrules,
const struct UFormattedNumber* number,
UChar *keyword, int32_t capacity,
UErrorCode *status);
#ifndef U_HIDE_INTERNAL_API
/**
* Given a number, returns the keyword of the first rule that applies to the
@ -160,7 +187,8 @@ uplrules_select(const UPluralRules *uplrules,
* @param fmt The UNumberFormat specifying how the number will be formatted
* (this can affect the plural form, e.g. "1 dollar" vs "1.0 dollars").
* If this is NULL, the function behaves like uplrules_select.
* @param keyword The keyword of the rule that applies to number.
* @param keyword An output buffer to write the keyword of the rule that
* applies to number.
* @param capacity The capacity of the keyword buffer.
* @param status A pointer to a UErrorCode to receive any errors.
* @return The length of keyword.

View file

@ -17,7 +17,9 @@
#include "unicode/unistr.h"
#include "unicode/unum.h"
#include "unicode/numfmt.h"
#include "unicode/unumberformatter.h"
#include "number_decimalquantity.h"
#include "number_utypes.h"
U_NAMESPACE_USE
@ -91,6 +93,28 @@ uplrules_select(const UPluralRules *uplrules,
return result.extract(keyword, capacity, *status);
}
U_CAPI int32_t U_EXPORT2
uplrules_selectFormatted(const UPluralRules *uplrules,
const UFormattedNumber* number,
UChar *keyword, int32_t capacity,
UErrorCode *status)
{
if (U_FAILURE(*status)) {
return 0;
}
if (keyword == NULL ? capacity != 0 : capacity < 0) {
*status = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
const number::impl::DecimalQuantity* dq =
number::impl::validateUFormattedNumberToDecimalQuantity(number, *status);
if (U_FAILURE(*status)) {
return 0;
}
UnicodeString result = ((PluralRules*)uplrules)->select(*dq);
return result.extract(keyword, capacity, *status);
}
U_CAPI int32_t U_EXPORT2
uplrules_selectWithFormat(const UPluralRules *uplrules,
double number,

View file

@ -13,6 +13,7 @@
#include "unicode/upluralrules.h"
#include "unicode/ustring.h"
#include "unicode/uenum.h"
#include "unicode/unumberformatter.h"
#include "cintltst.h"
#include "cmemory.h"
#include "cstring.h"
@ -20,6 +21,7 @@
static void TestPluralRules(void);
static void TestOrdinalRules(void);
static void TestGetKeywords(void);
static void TestFormatted(void);
void addPluralRulesTest(TestNode** root);
@ -30,6 +32,7 @@ void addPluralRulesTest(TestNode** root)
TESTCASE(TestPluralRules);
TESTCASE(TestOrdinalRules);
TESTCASE(TestGetKeywords);
TESTCASE(TestFormatted);
}
typedef struct {
@ -252,4 +255,44 @@ static void TestGetKeywords() {
}
}
static void TestFormatted() {
UErrorCode ec = U_ZERO_ERROR;
UNumberFormatter* unumf = NULL;
UFormattedNumber* uresult = NULL;
UPluralRules* uplrules = NULL;
uplrules = uplrules_open("hr", &ec);
if (!assertSuccess("open plural rules", &ec)) {
goto cleanup;
}
unumf = unumf_openForSkeletonAndLocale(u".00", -1, "hr", &ec);
if (!assertSuccess("open unumf", &ec)) {
goto cleanup;
}
uresult = unumf_openResult(&ec);
if (!assertSuccess("open result", &ec)) {
goto cleanup;
}
unumf_formatDouble(unumf, 100.2, uresult, &ec);
if (!assertSuccess("format", &ec)) {
goto cleanup;
}
UChar buffer[40];
uplrules_selectFormatted(uplrules, uresult, buffer, 40, &ec);
if (!assertSuccess("select", &ec)) {
goto cleanup;
}
assertUEquals("0.20 is plural category 'other' in hr", u"other", buffer);
cleanup:
uplrules_close(uplrules);
unumf_close(unumf);
unumf_closeResult(uresult);
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -840,7 +840,7 @@ library: i18n
dayperiodrules
listformatter
formatting formattable_cnv regex regex_cnv translit
double_conversion number_representation numberformatter numberparser
double_conversion number_representation number_output numberformatter numberparser
universal_time_scale
uclean_i18n
@ -937,6 +937,16 @@ group: number_representation
ucase uniset_core
formatted_value
group: number_output
# PluralRules and FormattedNumber
number_output.o
standardplural.o plurrule.o
deps
# FormattedNumber internals:
number_representation format
# PluralRules internals:
unifiedcache
group: numberformatter
# ICU 60+ NumberFormatter API
number_affixutils.o number_asformat.o
@ -950,11 +960,9 @@ group: numberformatter
number_scientific.o number_skeletons.o
currpinf.o dcfmtsym.o numsys.o
numrange_fluent.o numrange_impl.o
# pluralrules
standardplural.o plurrule.o
deps
decnumber double_conversion formattable format units
number_representation
decnumber double_conversion formattable units
number_representation number_output
uclean_i18n common
group: numberparser

View file

@ -6,7 +6,7 @@
* others. All Rights Reserved.
********************************************************************************
* File PLURRULTS.cpp
* File PLURULTS.cpp
*
********************************************************************************
*/
@ -22,6 +22,7 @@
#include "unicode/localpointer.h"
#include "unicode/plurrule.h"
#include "unicode/stringpiece.h"
#include "unicode/numberformatter.h"
#include "cmemory.h"
#include "plurrule_impl.h"
@ -53,6 +54,7 @@ void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &na
TESTCASE_AUTO(testAvailbleLocales);
TESTCASE_AUTO(testParseErrors);
TESTCASE_AUTO(testFixedDecimal);
TESTCASE_AUTO(testSelectTrailingZeros);
TESTCASE_AUTO_END;
}
@ -1004,5 +1006,36 @@ void PluralRulesTest::testFixedDecimal() {
}
void PluralRulesTest::testSelectTrailingZeros() {
IcuTestErrorCode status(*this, "testSelectTrailingZeros");
number::UnlocalizedNumberFormatter unf = number::NumberFormatter::with()
.precision(number::Precision::fixedFraction(2));
struct TestCase {
const char* localeName;
const char16_t* expectedDoubleKeyword;
const char16_t* expectedFormattedKeyword;
double number;
} cases[] = {
{"bs", u"few", u"other", 5.2}, // 5.2 => two, but 5.20 => other
{"si", u"one", u"one", 0.0},
{"si", u"one", u"one", 1.0},
{"si", u"one", u"other", 0.1}, // 0.1 => one, but 0.10 => other
{"si", u"one", u"one", 0.01}, // 0.01 => one
{"hsb", u"few", u"few", 1.03}, // (f % 100 == 3) => few
{"hsb", u"few", u"other", 1.3}, // 1.3 => few, but 1.30 => other
};
for (const auto& cas : cases) {
UnicodeString message(UnicodeString(cas.localeName) + u" " + DoubleToUnicodeString(cas.number));
status.setScope(message);
Locale locale(cas.localeName);
LocalPointer<PluralRules> rules(PluralRules::forLocale(locale, status));
assertEquals(message, cas.expectedDoubleKeyword, rules->select(cas.number));
number::FormattedNumber fn = unf.locale(locale).formatDouble(cas.number, status);
assertEquals(message, cas.expectedFormattedKeyword, rules->select(fn, status));
status.errIfFailureAndReset();
}
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -37,6 +37,7 @@ private:
void testAvailbleLocales();
void testParseErrors();
void testFixedDecimal();
void testSelectTrailingZeros();
void assertRuleValue(const UnicodeString& rule, double expected);
void assertRuleKeyValue(const UnicodeString& rule, const UnicodeString& key,

View file

@ -29,6 +29,8 @@ import java.util.TreeSet;
import java.util.regex.Pattern;
import com.ibm.icu.impl.PluralRulesLoader;
import com.ibm.icu.number.FormattedNumber;
import com.ibm.icu.number.NumberFormatter;
import com.ibm.icu.util.Output;
import com.ibm.icu.util.ULocale;
@ -2026,9 +2028,10 @@ public class PluralRules implements Serializable {
public int hashCode() {
return rules.hashCode();
}
/**
* Given a number, returns the keyword of the first rule that applies to
* the number.
* Given a floating-point number, returns the keyword of the first rule
* that applies to the number.
*
* @param number The number for which the rule has to be determined.
* @return The keyword of the selected rule.
@ -2038,6 +2041,23 @@ public class PluralRules implements Serializable {
return rules.select(new FixedDecimal(number));
}
/**
* Given a formatted number, returns the keyword of the first rule that
* applies to the number.
*
* A FormattedNumber allows you to specify an exponent or trailing zeros,
* which can affect the plural category. To get a FormattedNumber, see
* {@link NumberFormatter}.
*
* @param number The number for which the rule has to be determined.
* @return The keyword of the selected rule.
* @draft ICU 64
* @provisional This API might change or be removed in a future release.
*/
public String select(FormattedNumber number) {
return rules.select(number.getFixedDecimal());
}
/**
* Given a number, returns the keyword of the first rule that applies to
* the number.

View file

@ -41,6 +41,10 @@ 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.number.FormattedNumber;
import com.ibm.icu.number.NumberFormatter;
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.FixedDecimal;
@ -323,8 +327,8 @@ public class PluralRulesTest extends TestFmwk {
public void testUniqueRules() {
main: for (ULocale locale : factory.getAvailableULocales()) {
PluralRules rules = factory.forLocale(locale);
Map<String, PluralRules> keywordToRule = new HashMap<String, PluralRules>();
Collection<FixedDecimalSamples> samples = new LinkedHashSet<FixedDecimalSamples>();
Map<String, PluralRules> keywordToRule = new HashMap<>();
Collection<FixedDecimalSamples> samples = new LinkedHashSet<>();
for (String keyword : rules.getKeywords()) {
for (SampleType sampleType : SampleType.values()) {
@ -467,21 +471,59 @@ public class PluralRulesTest extends TestFmwk {
@Test
public void testBuiltInRules() {
// spot check
PluralRules rules = factory.forLocale(ULocale.US);
assertEquals("us 0", PluralRules.KEYWORD_OTHER, rules.select(0));
assertEquals("us 1", PluralRules.KEYWORD_ONE, rules.select(1));
assertEquals("us 2", PluralRules.KEYWORD_OTHER, rules.select(2));
Object[][] cases = {
{"en-US", PluralRules.KEYWORD_OTHER, 0},
{"en-US", PluralRules.KEYWORD_ONE, 1},
{"en-US", PluralRules.KEYWORD_OTHER, 2},
{"ja-JP", PluralRules.KEYWORD_OTHER, 0},
{"ja-JP", PluralRules.KEYWORD_OTHER, 1},
{"ja-JP", PluralRules.KEYWORD_OTHER, 2},
{"ru", PluralRules.KEYWORD_MANY, 0},
{"ru", PluralRules.KEYWORD_ONE, 1},
{"ru", PluralRules.KEYWORD_FEW, 2}
};
for (Object[] cas : cases) {
ULocale locale = new ULocale((String) cas[0]);
PluralRules rules = factory.forLocale(locale);
String expectedKeyword = (String) cas[1];
double number = (Integer) cas[2];
String message = locale + " " + number;
// Check both as double and as FormattedNumber.
assertEquals(message, expectedKeyword, rules.select(number));
FormattedNumber fn = NumberFormatter.withLocale(locale).format(number);
assertEquals(message, expectedKeyword, rules.select(fn));
}
}
rules = factory.forLocale(ULocale.JAPAN);
assertEquals("ja 0", PluralRules.KEYWORD_OTHER, rules.select(0));
assertEquals("ja 1", PluralRules.KEYWORD_OTHER, rules.select(1));
assertEquals("ja 2", PluralRules.KEYWORD_OTHER, rules.select(2));
rules = factory.forLocale(ULocale.createCanonical("ru"));
assertEquals("ru 0", PluralRules.KEYWORD_MANY, rules.select(0));
assertEquals("ru 1", PluralRules.KEYWORD_ONE, rules.select(1));
assertEquals("ru 2", PluralRules.KEYWORD_FEW, rules.select(2));
@Test
public void testSelectTrailingZeros() {
UnlocalizedNumberFormatter unf = NumberFormatter.with()
.precision(Precision.fixedFraction(2));
Object[][] cases = {
// 1) locale
// 2) double expected keyword
// 3) formatted number expected keyword (2 fraction digits)
// 4) input number
{"bs", PluralRules.KEYWORD_FEW, PluralRules.KEYWORD_OTHER, 5.2}, // 5.2 => two, but 5.20 => other
{"si", PluralRules.KEYWORD_ONE, PluralRules.KEYWORD_ONE, 0.0},
{"si", PluralRules.KEYWORD_ONE, PluralRules.KEYWORD_ONE, 1.0},
{"si", PluralRules.KEYWORD_ONE, PluralRules.KEYWORD_OTHER, 0.1}, // 0.1 => one, but 0.10 => other
{"si", PluralRules.KEYWORD_ONE, PluralRules.KEYWORD_ONE, 0.01}, // 0.01 => one
{"hsb", PluralRules.KEYWORD_FEW, PluralRules.KEYWORD_FEW, 1.03}, // (f % 100 == 3) => few
{"hsb", PluralRules.KEYWORD_FEW, PluralRules.KEYWORD_OTHER, 1.3}, // 1.3 => few, but 1.30 => other
};
for (Object[] cas : cases) {
ULocale locale = new ULocale((String) cas[0]);
PluralRules rules = factory.forLocale(locale);
String expectedDoubleKeyword = (String) cas[1];
String expectedFormattedKeyword = (String) cas[2];
double number = (Double) cas[3];
String message = locale + " " + number;
// Check both as double and as FormattedNumber.
assertEquals(message, expectedDoubleKeyword, rules.select(number));
FormattedNumber fn = unf.locale(locale).format(number);
assertEquals(message, expectedFormattedKeyword, rules.select(fn));
}
}
@Test
@ -605,7 +647,7 @@ public class PluralRulesTest extends TestFmwk {
*/
@Test
public void TestGetSamples() {
Set<ULocale> uniqueRuleSet = new HashSet<ULocale>();
Set<ULocale> uniqueRuleSet = new HashSet<>();
for (ULocale locale : factory.getAvailableULocales()) {
uniqueRuleSet.add(PluralRules.getFunctionalEquivalent(locale, null));
}
@ -719,7 +761,7 @@ public class PluralRulesTest extends TestFmwk {
} else if ("null".equals(valueList)) {
values = null;
} else {
values = new TreeSet<Double>();
values = new TreeSet<>();
for (String value : valueList.split(",")) {
values.add(Double.parseDouble(value));
}
@ -825,9 +867,9 @@ public class PluralRulesTest extends TestFmwk {
// suppressed in
// INTEGER but not
// DECIMAL
}, { { "en", new HashSet<Double>(Arrays.asList(1.0d)) }, // check that 1 is suppressed
}, { { "en", new HashSet<>(Arrays.asList(1.0d)) }, // check that 1 is suppressed
{ "one", KeywordStatus.SUPPRESSED, null }, { "other", KeywordStatus.UNBOUNDED, null } }, };
Output<Double> uniqueValue = new Output<Double>();
Output<Double> uniqueValue = new Output<>();
for (Object[][] test : tests) {
ULocale locale = new ULocale((String) test[0][0]);
// NumberType numberType = (NumberType) test[1];
@ -938,7 +980,7 @@ public class PluralRulesTest extends TestFmwk {
};
private void generateLOCALE_SNAPSHOT() {
Comparator c = new CollectionUtilities.CollectionComparator<Comparable>();
Comparator c = new CollectionUtilities.CollectionComparator<>();
Relation<Set<StandardPluralCategories>, PluralRules> setsToRules = Relation.of(
new TreeMap<Set<StandardPluralCategories>, Set<PluralRules>>(c), TreeSet.class, PLURAL_RULE_COMPARATOR);
Relation<PluralRules, ULocale> data = Relation.of(
@ -1054,7 +1096,7 @@ public class PluralRulesTest extends TestFmwk {
@Test
public void TestSerialization() {
Output<Integer> size = new Output<Integer>();
Output<Integer> size = new Output<>();
int max = 0;
for (ULocale locale : PluralRules.getAvailableULocales()) {
PluralRules item = PluralRules.forLocale(locale);