mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-06 22:15:31 +00:00
parent
12dc3772b1
commit
6b3a7a64ab
28 changed files with 762 additions and 859 deletions
|
@ -221,6 +221,7 @@
|
|||
<ClCompile Include="numsys.cpp" />
|
||||
<ClCompile Include="olsontz.cpp" />
|
||||
<ClCompile Include="persncal.cpp" />
|
||||
<ClCompile Include="pluralranges.cpp" />
|
||||
<ClCompile Include="plurfmt.cpp" />
|
||||
<ClCompile Include="plurrule.cpp" />
|
||||
<ClCompile Include="quantityformatter.cpp" />
|
||||
|
@ -391,6 +392,7 @@
|
|||
<ClInclude Include="nfsubs.h" />
|
||||
<ClInclude Include="olsontz.h" />
|
||||
<ClInclude Include="persncal.h" />
|
||||
<ClInclude Include="pluralranges.h" />
|
||||
<ClInclude Include="plurrule_impl.h" />
|
||||
<ClInclude Include="quantityformatter.h" />
|
||||
<ClInclude Include="sharedbreakiterator.h" />
|
||||
|
|
|
@ -234,6 +234,9 @@
|
|||
<ClCompile Include="persncal.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pluralranges.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="plurfmt.cpp">
|
||||
<Filter>formatting</Filter>
|
||||
</ClCompile>
|
||||
|
@ -983,6 +986,9 @@
|
|||
<ClInclude Include="persncal.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pluralranges.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="plurrule_impl.h">
|
||||
<Filter>formatting</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -454,6 +454,7 @@
|
|||
<ClCompile Include="numsys.cpp" />
|
||||
<ClCompile Include="olsontz.cpp" />
|
||||
<ClCompile Include="persncal.cpp" />
|
||||
<ClCompile Include="pluralranges.cpp" />
|
||||
<ClCompile Include="plurfmt.cpp" />
|
||||
<ClCompile Include="plurrule.cpp" />
|
||||
<ClCompile Include="quantityformatter.cpp" />
|
||||
|
@ -622,6 +623,7 @@
|
|||
<ClInclude Include="nfsubs.h" />
|
||||
<ClInclude Include="olsontz.h" />
|
||||
<ClInclude Include="persncal.h" />
|
||||
<ClInclude Include="pluralranges.h" />
|
||||
<ClInclude Include="plurrule_impl.h" />
|
||||
<ClInclude Include="quantityformatter.h" />
|
||||
<ClInclude Include="sharedbreakiterator.h" />
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "util.h"
|
||||
#include "number_decimalquantity.h"
|
||||
#include "number_decnum.h"
|
||||
#include "numrange_impl.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
namespace number {
|
||||
|
@ -47,6 +48,42 @@ void FormattedNumber::getDecimalQuantity(impl::DecimalQuantity& output, UErrorCo
|
|||
impl::UFormattedNumberData::~UFormattedNumberData() = default;
|
||||
|
||||
|
||||
UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumberRange)
|
||||
|
||||
#define UPRV_NOARG
|
||||
|
||||
UnicodeString FormattedNumberRange::getFirstDecimal(UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString())
|
||||
return fData->quantity1.toScientificString();
|
||||
}
|
||||
|
||||
UnicodeString FormattedNumberRange::getSecondDecimal(UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString())
|
||||
return fData->quantity2.toScientificString();
|
||||
}
|
||||
|
||||
void FormattedNumberRange::getDecimalNumbers(ByteSink& sink1, ByteSink& sink2, UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG)
|
||||
impl::DecNum decnum1;
|
||||
impl::DecNum decnum2;
|
||||
fData->quantity1.toDecNum(decnum1, status).toString(sink1, status);
|
||||
fData->quantity2.toDecNum(decnum2, status).toString(sink2, status);
|
||||
}
|
||||
|
||||
UNumberRangeIdentityResult FormattedNumberRange::getIdentityResult(UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(UNUM_IDENTITY_RESULT_NOT_EQUAL)
|
||||
return fData->identityResult;
|
||||
}
|
||||
|
||||
const impl::UFormattedNumberRangeData* FormattedNumberRange::getData(UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(nullptr)
|
||||
return fData;
|
||||
}
|
||||
|
||||
|
||||
impl::UFormattedNumberRangeData::~UFormattedNumberRangeData() = default;
|
||||
|
||||
|
||||
} // namespace number
|
||||
U_NAMESPACE_END
|
||||
|
||||
|
|
|
@ -59,8 +59,8 @@ UFormattedNumberRangeImpl::~UFormattedNumberRangeImpl() {
|
|||
fImpl.fData = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace impl
|
||||
} // namespace number
|
||||
U_NAMESPACE_END
|
||||
|
||||
|
||||
|
@ -71,6 +71,16 @@ UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(
|
|||
unumrf)
|
||||
|
||||
|
||||
const UFormattedNumberRangeData* number::impl::validateUFormattedNumberRange(
|
||||
const UFormattedNumberRange* uresult, UErrorCode& status) {
|
||||
auto* result = UFormattedNumberRangeApiHelper::validate(uresult, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
return &result->fData;
|
||||
}
|
||||
|
||||
|
||||
U_CAPI UNumberRangeFormatter* U_EXPORT2
|
||||
unumrf_openForSkeletonWithCollapseAndIdentityFallback(
|
||||
const UChar* skeleton,
|
||||
|
|
|
@ -376,36 +376,4 @@ LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const {
|
|||
}
|
||||
|
||||
|
||||
UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumberRange)
|
||||
|
||||
#define UPRV_NOARG
|
||||
|
||||
UnicodeString FormattedNumberRange::getFirstDecimal(UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString())
|
||||
return fData->quantity1.toScientificString();
|
||||
}
|
||||
|
||||
UnicodeString FormattedNumberRange::getSecondDecimal(UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString())
|
||||
return fData->quantity2.toScientificString();
|
||||
}
|
||||
|
||||
void FormattedNumberRange::getDecimalNumbers(ByteSink& sink1, ByteSink& sink2, UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG)
|
||||
impl::DecNum decnum1;
|
||||
impl::DecNum decnum2;
|
||||
fData->quantity1.toDecNum(decnum1, status).toString(sink1, status);
|
||||
fData->quantity2.toDecNum(decnum2, status).toString(sink2, status);
|
||||
}
|
||||
|
||||
UNumberRangeIdentityResult FormattedNumberRange::getIdentityResult(UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(UNUM_IDENTITY_RESULT_NOT_EQUAL)
|
||||
return fData->identityResult;
|
||||
}
|
||||
|
||||
|
||||
UFormattedNumberRangeData::~UFormattedNumberRangeData() = default;
|
||||
|
||||
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "unicode/numberrangeformatter.h"
|
||||
#include "numrange_impl.h"
|
||||
#include "patternprops.h"
|
||||
#include "pluralranges.h"
|
||||
#include "uresimp.h"
|
||||
#include "util.h"
|
||||
|
||||
|
@ -106,92 +107,9 @@ void getNumberRangeData(const char* localeName, const char* nsName, NumberRangeD
|
|||
sink.fillInDefaults(status);
|
||||
}
|
||||
|
||||
class PluralRangesDataSink : public ResourceSink {
|
||||
public:
|
||||
PluralRangesDataSink(StandardPluralRanges& output) : fOutput(output) {}
|
||||
|
||||
void put(const char* /*key*/, ResourceValue& value, UBool /*noFallback*/, UErrorCode& status) U_OVERRIDE {
|
||||
ResourceArray entriesArray = value.getArray(status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
fOutput.setCapacity(entriesArray.getSize());
|
||||
for (int i = 0; entriesArray.getValue(i, value); i++) {
|
||||
ResourceArray pluralFormsArray = value.getArray(status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
pluralFormsArray.getValue(0, value);
|
||||
StandardPlural::Form first = StandardPlural::fromString(value.getUnicodeString(status), status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
pluralFormsArray.getValue(1, value);
|
||||
StandardPlural::Form second = StandardPlural::fromString(value.getUnicodeString(status), status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
pluralFormsArray.getValue(2, value);
|
||||
StandardPlural::Form result = StandardPlural::fromString(value.getUnicodeString(status), status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
fOutput.addPluralRange(first, second, result);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
StandardPluralRanges& fOutput;
|
||||
};
|
||||
|
||||
void getPluralRangesData(const Locale& locale, StandardPluralRanges& output, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) { return; }
|
||||
LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "pluralRanges", &status));
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
CharString dataPath;
|
||||
dataPath.append("locales/", -1, status);
|
||||
dataPath.append(locale.getLanguage(), -1, status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
int32_t setLen;
|
||||
// Not all languages are covered: fail gracefully
|
||||
UErrorCode internalStatus = U_ZERO_ERROR;
|
||||
const UChar* set = ures_getStringByKeyWithFallback(rb.getAlias(), dataPath.data(), &setLen, &internalStatus);
|
||||
if (U_FAILURE(internalStatus)) { return; }
|
||||
|
||||
dataPath.clear();
|
||||
dataPath.append("rules/", -1, status);
|
||||
dataPath.appendInvariantChars(set, setLen, status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
PluralRangesDataSink sink(output);
|
||||
ures_getAllItemsWithFallback(rb.getAlias(), dataPath.data(), sink, status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
void StandardPluralRanges::initialize(const Locale& locale, UErrorCode& status) {
|
||||
getPluralRangesData(locale, *this, status);
|
||||
}
|
||||
|
||||
void StandardPluralRanges::addPluralRange(
|
||||
StandardPlural::Form first,
|
||||
StandardPlural::Form second,
|
||||
StandardPlural::Form result) {
|
||||
U_ASSERT(fTriplesLen < fTriples.getCapacity());
|
||||
fTriples[fTriplesLen] = {first, second, result};
|
||||
fTriplesLen++;
|
||||
}
|
||||
|
||||
void StandardPluralRanges::setCapacity(int32_t length) {
|
||||
if (length > fTriples.getCapacity()) {
|
||||
fTriples.resize(length, 0);
|
||||
}
|
||||
}
|
||||
|
||||
StandardPlural::Form
|
||||
StandardPluralRanges::resolve(StandardPlural::Form first, StandardPlural::Form second) const {
|
||||
for (int32_t i=0; i<fTriplesLen; i++) {
|
||||
const auto& triple = fTriples[i];
|
||||
if (triple.first == first && triple.second == second) {
|
||||
return triple.result;
|
||||
}
|
||||
}
|
||||
// Default fallback
|
||||
return StandardPlural::OTHER;
|
||||
}
|
||||
|
||||
|
||||
NumberRangeFormatterImpl::NumberRangeFormatterImpl(const RangeMacroProps& macros, UErrorCode& status)
|
||||
: formatterImpl1(macros.formatter1.fMacros, status),
|
||||
|
@ -213,7 +131,7 @@ NumberRangeFormatterImpl::NumberRangeFormatterImpl(const RangeMacroProps& macros
|
|||
fApproximatelyModifier = {data.approximatelyPattern, kUndefinedField, false};
|
||||
|
||||
// TODO: Get locale from PluralRules instead?
|
||||
fPluralRanges.initialize(macros.locale, status);
|
||||
fPluralRanges = StandardPluralRanges::forLocale(macros.locale, status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "number_formatimpl.h"
|
||||
#include "formatted_string_builder.h"
|
||||
#include "formattedval_impl.h"
|
||||
#include "pluralranges.h"
|
||||
|
||||
U_NAMESPACE_BEGIN namespace number {
|
||||
namespace impl {
|
||||
|
@ -40,36 +41,6 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class StandardPluralRanges : public UMemory {
|
||||
public:
|
||||
void initialize(const Locale& locale, UErrorCode& status);
|
||||
StandardPlural::Form resolve(StandardPlural::Form first, StandardPlural::Form second) const;
|
||||
|
||||
/** Used for data loading. */
|
||||
void addPluralRange(
|
||||
StandardPlural::Form first,
|
||||
StandardPlural::Form second,
|
||||
StandardPlural::Form result);
|
||||
|
||||
/** Used for data loading. */
|
||||
void setCapacity(int32_t length);
|
||||
|
||||
private:
|
||||
struct StandardPluralRangeTriple {
|
||||
StandardPlural::Form first;
|
||||
StandardPlural::Form second;
|
||||
StandardPlural::Form result;
|
||||
};
|
||||
|
||||
// TODO: An array is simple here, but it results in linear lookup time.
|
||||
// Certain locales have 20-30 entries in this list.
|
||||
// Consider changing to a smarter data structure.
|
||||
typedef MaybeStackArray<StandardPluralRangeTriple, 3> PluralRangeTriples;
|
||||
PluralRangeTriples fTriples;
|
||||
int32_t fTriplesLen = 0;
|
||||
};
|
||||
|
||||
|
||||
class NumberRangeFormatterImpl : public UMemory {
|
||||
public:
|
||||
NumberRangeFormatterImpl(const RangeMacroProps& macros, UErrorCode& status);
|
||||
|
@ -105,6 +76,11 @@ class NumberRangeFormatterImpl : public UMemory {
|
|||
};
|
||||
|
||||
|
||||
/** Helper function used in upluralrules.cpp */
|
||||
const UFormattedNumberRangeData* validateUFormattedNumberRange(
|
||||
const UFormattedNumberRange* uresult, UErrorCode& status);
|
||||
|
||||
|
||||
} // namespace impl
|
||||
} // namespace number
|
||||
U_NAMESPACE_END
|
||||
|
|
144
icu4c/source/i18n/pluralranges.cpp
Normal file
144
icu4c/source/i18n/pluralranges.cpp
Normal file
|
@ -0,0 +1,144 @@
|
|||
// © 2018 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
// Allow implicit conversion from char16_t* to UnicodeString for this file:
|
||||
// Helpful in toString methods and elsewhere.
|
||||
#define UNISTR_FROM_STRING_EXPLICIT
|
||||
|
||||
#include "unicode/numberrangeformatter.h"
|
||||
#include "pluralranges.h"
|
||||
#include "uresimp.h"
|
||||
#include "charstr.h"
|
||||
#include "uassert.h"
|
||||
#include "util.h"
|
||||
#include "numrange_impl.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
class PluralRangesDataSink : public ResourceSink {
|
||||
public:
|
||||
PluralRangesDataSink(StandardPluralRanges& output) : fOutput(output) {}
|
||||
|
||||
void put(const char* /*key*/, ResourceValue& value, UBool /*noFallback*/, UErrorCode& status) U_OVERRIDE {
|
||||
ResourceArray entriesArray = value.getArray(status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
fOutput.setCapacity(entriesArray.getSize(), status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
for (int i = 0; entriesArray.getValue(i, value); i++) {
|
||||
ResourceArray pluralFormsArray = value.getArray(status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
if (pluralFormsArray.getSize() != 3) {
|
||||
status = U_RESOURCE_TYPE_MISMATCH;
|
||||
return;
|
||||
}
|
||||
pluralFormsArray.getValue(0, value);
|
||||
StandardPlural::Form first = StandardPlural::fromString(value.getUnicodeString(status), status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
pluralFormsArray.getValue(1, value);
|
||||
StandardPlural::Form second = StandardPlural::fromString(value.getUnicodeString(status), status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
pluralFormsArray.getValue(2, value);
|
||||
StandardPlural::Form result = StandardPlural::fromString(value.getUnicodeString(status), status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
fOutput.addPluralRange(first, second, result);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
StandardPluralRanges& fOutput;
|
||||
};
|
||||
|
||||
void getPluralRangesData(const Locale& locale, StandardPluralRanges& output, UErrorCode& status) {
|
||||
LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "pluralRanges", &status));
|
||||
if (U_FAILURE(status)) { return; }
|
||||
|
||||
CharString dataPath;
|
||||
dataPath.append("locales/", -1, status);
|
||||
dataPath.append(locale.getLanguage(), -1, status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
int32_t setLen;
|
||||
// Not all languages are covered: fail gracefully
|
||||
UErrorCode internalStatus = U_ZERO_ERROR;
|
||||
const UChar* set = ures_getStringByKeyWithFallback(rb.getAlias(), dataPath.data(), &setLen, &internalStatus);
|
||||
if (U_FAILURE(internalStatus)) { return; }
|
||||
|
||||
dataPath.clear();
|
||||
dataPath.append("rules/", -1, status);
|
||||
dataPath.appendInvariantChars(set, setLen, status);
|
||||
if (U_FAILURE(status)) { return; }
|
||||
PluralRangesDataSink sink(output);
|
||||
ures_getAllItemsWithFallback(rb.getAlias(), dataPath.data(), sink, status);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
StandardPluralRanges
|
||||
StandardPluralRanges::forLocale(const Locale& locale, UErrorCode& status) {
|
||||
StandardPluralRanges result;
|
||||
getPluralRangesData(locale, result, status);
|
||||
return result;
|
||||
}
|
||||
|
||||
StandardPluralRanges
|
||||
StandardPluralRanges::copy(UErrorCode& status) const {
|
||||
StandardPluralRanges result;
|
||||
if (fTriplesLen > result.fTriples.getCapacity()) {
|
||||
if (result.fTriples.resize(fTriplesLen) == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
uprv_memcpy(result.fTriples.getAlias(),
|
||||
fTriples.getAlias(),
|
||||
fTriplesLen * sizeof(fTriples[0]));
|
||||
result.fTriplesLen = fTriplesLen;
|
||||
return result;
|
||||
}
|
||||
|
||||
LocalPointer<StandardPluralRanges>
|
||||
StandardPluralRanges::toPointer(UErrorCode& status) && noexcept {
|
||||
return LocalPointer<StandardPluralRanges>(new StandardPluralRanges(std::move(*this)), status);
|
||||
}
|
||||
|
||||
void StandardPluralRanges::addPluralRange(
|
||||
StandardPlural::Form first,
|
||||
StandardPlural::Form second,
|
||||
StandardPlural::Form result) {
|
||||
U_ASSERT(fTriplesLen < fTriples.getCapacity());
|
||||
fTriples[fTriplesLen] = {first, second, result};
|
||||
fTriplesLen++;
|
||||
}
|
||||
|
||||
void StandardPluralRanges::setCapacity(int32_t length, UErrorCode& status) {
|
||||
if (U_FAILURE(status)) { return; }
|
||||
if (length > fTriples.getCapacity()) {
|
||||
if (fTriples.resize(length, 0) == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StandardPlural::Form
|
||||
StandardPluralRanges::resolve(StandardPlural::Form first, StandardPlural::Form second) const {
|
||||
for (int32_t i=0; i<fTriplesLen; i++) {
|
||||
const auto& triple = fTriples[i];
|
||||
if (triple.first == first && triple.second == second) {
|
||||
return triple.result;
|
||||
}
|
||||
}
|
||||
// Default fallback
|
||||
return StandardPlural::OTHER;
|
||||
}
|
||||
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
67
icu4c/source/i18n/pluralranges.h
Normal file
67
icu4c/source/i18n/pluralranges.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
// © 2018 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
|
||||
#ifndef __PLURALRANGES_H__
|
||||
#define __PLURALRANGES_H__
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "unicode/uobject.h"
|
||||
#include "unicode/locid.h"
|
||||
#include "unicode/plurrule.h"
|
||||
#include "standardplural.h"
|
||||
#include "cmemory.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
// Forward declarations
|
||||
namespace number {
|
||||
namespace impl {
|
||||
class UFormattedNumberRangeData;
|
||||
}
|
||||
}
|
||||
|
||||
class StandardPluralRanges : public UMemory {
|
||||
public:
|
||||
/** Create a new StandardPluralRanges for the given locale */
|
||||
static StandardPluralRanges forLocale(const Locale& locale, UErrorCode& status);
|
||||
|
||||
/** Explicit copy constructor */
|
||||
StandardPluralRanges copy(UErrorCode& status) const;
|
||||
|
||||
/** Create an object (called on an rvalue) */
|
||||
LocalPointer<StandardPluralRanges> toPointer(UErrorCode& status) && noexcept;
|
||||
|
||||
/** Select rule based on the first and second forms */
|
||||
StandardPlural::Form resolve(StandardPlural::Form first, StandardPlural::Form second) const;
|
||||
|
||||
/** Used for data loading. */
|
||||
void addPluralRange(
|
||||
StandardPlural::Form first,
|
||||
StandardPlural::Form second,
|
||||
StandardPlural::Form result);
|
||||
|
||||
/** Used for data loading. */
|
||||
void setCapacity(int32_t length, UErrorCode& status);
|
||||
|
||||
private:
|
||||
struct StandardPluralRangeTriple {
|
||||
StandardPlural::Form first;
|
||||
StandardPlural::Form second;
|
||||
StandardPlural::Form result;
|
||||
};
|
||||
|
||||
// TODO: An array is simple here, but it results in linear lookup time.
|
||||
// Certain locales have 20-30 entries in this list.
|
||||
// Consider changing to a smarter data structure.
|
||||
typedef MaybeStackArray<StandardPluralRangeTriple, 3> PluralRangeTriples;
|
||||
PluralRangeTriples fTriples;
|
||||
int32_t fTriplesLen = 0;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
#endif //__PLURALRANGES_H__
|
|
@ -19,6 +19,7 @@
|
|||
#include "unicode/ures.h"
|
||||
#include "unicode/numfmt.h"
|
||||
#include "unicode/decimfmt.h"
|
||||
#include "unicode/numberrangeformatter.h"
|
||||
#include "charstr.h"
|
||||
#include "cmemory.h"
|
||||
#include "cstring.h"
|
||||
|
@ -36,6 +37,8 @@
|
|||
#include "unifiedcache.h"
|
||||
#include "number_decimalquantity.h"
|
||||
#include "util.h"
|
||||
#include "pluralranges.h"
|
||||
#include "numrange_impl.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
|
@ -68,6 +71,7 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration)
|
|||
PluralRules::PluralRules(UErrorCode& /*status*/)
|
||||
: UObject(),
|
||||
mRules(nullptr),
|
||||
mStandardPluralRanges(nullptr),
|
||||
mInternalStatus(U_ZERO_ERROR)
|
||||
{
|
||||
}
|
||||
|
@ -75,6 +79,7 @@ PluralRules::PluralRules(UErrorCode& /*status*/)
|
|||
PluralRules::PluralRules(const PluralRules& other)
|
||||
: UObject(other),
|
||||
mRules(nullptr),
|
||||
mStandardPluralRanges(nullptr),
|
||||
mInternalStatus(U_ZERO_ERROR)
|
||||
{
|
||||
*this=other;
|
||||
|
@ -82,6 +87,7 @@ PluralRules::PluralRules(const PluralRules& other)
|
|||
|
||||
PluralRules::~PluralRules() {
|
||||
delete mRules;
|
||||
delete mStandardPluralRanges;
|
||||
}
|
||||
|
||||
SharedPluralRules::~SharedPluralRules() {
|
||||
|
@ -90,14 +96,20 @@ SharedPluralRules::~SharedPluralRules() {
|
|||
|
||||
PluralRules*
|
||||
PluralRules::clone() const {
|
||||
PluralRules* newObj = new PluralRules(*this);
|
||||
// Since clone doesn't have a 'status' parameter, the best we can do is return nullptr if
|
||||
// the newly created object was not fully constructed properly (an error occurred).
|
||||
if (newObj != nullptr && U_FAILURE(newObj->mInternalStatus)) {
|
||||
delete newObj;
|
||||
newObj = nullptr;
|
||||
UErrorCode localStatus = U_ZERO_ERROR;
|
||||
return clone(localStatus);
|
||||
}
|
||||
|
||||
PluralRules*
|
||||
PluralRules::clone(UErrorCode& status) const {
|
||||
LocalPointer<PluralRules> newObj(new PluralRules(*this), status);
|
||||
if (U_SUCCESS(status) && U_FAILURE(newObj->mInternalStatus)) {
|
||||
status = newObj->mInternalStatus;
|
||||
newObj.adoptInstead(nullptr);
|
||||
}
|
||||
return newObj;
|
||||
return newObj.orphan();
|
||||
}
|
||||
|
||||
PluralRules&
|
||||
|
@ -105,6 +117,8 @@ PluralRules::operator=(const PluralRules& other) {
|
|||
if (this != &other) {
|
||||
delete mRules;
|
||||
mRules = nullptr;
|
||||
delete mStandardPluralRanges;
|
||||
mStandardPluralRanges = nullptr;
|
||||
mInternalStatus = other.mInternalStatus;
|
||||
if (U_FAILURE(mInternalStatus)) {
|
||||
// bail out early if the object we were copying from was already 'invalid'.
|
||||
|
@ -120,6 +134,11 @@ PluralRules::operator=(const PluralRules& other) {
|
|||
mInternalStatus = mRules->fInternalStatus;
|
||||
}
|
||||
}
|
||||
if (other.mStandardPluralRanges != nullptr) {
|
||||
mStandardPluralRanges = other.mStandardPluralRanges->copy(mInternalStatus)
|
||||
.toPointer(mInternalStatus)
|
||||
.orphan();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@ -212,11 +231,8 @@ PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& statu
|
|||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
PluralRules *result = (*shared)->clone();
|
||||
PluralRules *result = (*shared)->clone(status);
|
||||
shared->removeRef();
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -253,6 +269,10 @@ PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCod
|
|||
// Original impl used default rules.
|
||||
// Ask the question to ICU Core.
|
||||
|
||||
newObj->mStandardPluralRanges = StandardPluralRanges::forLocale(locale, status)
|
||||
.toPointer(status)
|
||||
.orphan();
|
||||
|
||||
return newObj.orphan();
|
||||
}
|
||||
|
||||
|
@ -273,6 +293,10 @@ PluralRules::select(const number::FormattedNumber& number, UErrorCode& status) c
|
|||
if (U_FAILURE(status)) {
|
||||
return ICU_Utility::makeBogusString();
|
||||
}
|
||||
if (U_FAILURE(mInternalStatus)) {
|
||||
status = mInternalStatus;
|
||||
return ICU_Utility::makeBogusString();
|
||||
}
|
||||
return select(dq);
|
||||
}
|
||||
|
||||
|
@ -286,6 +310,33 @@ PluralRules::select(const IFixedDecimal &number) const {
|
|||
}
|
||||
}
|
||||
|
||||
UnicodeString
|
||||
PluralRules::select(const number::FormattedNumberRange& range, UErrorCode& status) const {
|
||||
return select(range.getData(status), status);
|
||||
}
|
||||
|
||||
UnicodeString
|
||||
PluralRules::select(const number::impl::UFormattedNumberRangeData* impl, UErrorCode& status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return ICU_Utility::makeBogusString();
|
||||
}
|
||||
if (U_FAILURE(mInternalStatus)) {
|
||||
status = mInternalStatus;
|
||||
return ICU_Utility::makeBogusString();
|
||||
}
|
||||
if (mStandardPluralRanges == nullptr) {
|
||||
// Happens if PluralRules was constructed via createRules()
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return ICU_Utility::makeBogusString();
|
||||
}
|
||||
auto form1 = StandardPlural::fromString(select(impl->quantity1), status);
|
||||
auto form2 = StandardPlural::fromString(select(impl->quantity2), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return ICU_Utility::makeBogusString();
|
||||
}
|
||||
auto result = mStandardPluralRanges->resolve(form1, form2);
|
||||
return UnicodeString(StandardPlural::getKeyword(result), -1, US_INV);
|
||||
}
|
||||
|
||||
|
||||
StringEnumeration*
|
||||
|
|
|
@ -140,6 +140,7 @@ numrange_impl.cpp
|
|||
numsys.cpp
|
||||
olsontz.cpp
|
||||
persncal.cpp
|
||||
pluralranges.cpp
|
||||
plurfmt.cpp
|
||||
plurrule.cpp
|
||||
quant.cpp
|
||||
|
|
|
@ -47,6 +47,9 @@
|
|||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
// Forward declarations:
|
||||
class PluralRules;
|
||||
|
||||
namespace number { // icu::number
|
||||
|
||||
// Forward declarations:
|
||||
|
@ -733,6 +736,11 @@ class U_I18N_API FormattedNumberRange : public UMemory, public FormattedValue {
|
|||
|
||||
void getDecimalNumbers(ByteSink& sink1, ByteSink& sink2, UErrorCode& status) const;
|
||||
|
||||
const impl::UFormattedNumberRangeData* getData(UErrorCode& status) const;
|
||||
|
||||
// To allow PluralRules to access the underlying data
|
||||
friend class ::icu::PluralRules;
|
||||
|
||||
// To give LocalizedNumberRangeFormatter format methods access to this class's constructor:
|
||||
friend class LocalizedNumberRangeFormatter;
|
||||
|
||||
|
|
|
@ -51,9 +51,14 @@ class PluralRuleParser;
|
|||
class PluralKeywordEnumeration;
|
||||
class AndConstraint;
|
||||
class SharedPluralRules;
|
||||
class StandardPluralRanges;
|
||||
|
||||
namespace number {
|
||||
class FormattedNumber;
|
||||
class FormattedNumberRange;
|
||||
namespace impl {
|
||||
class UFormattedNumberRangeData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -367,11 +372,35 @@ public:
|
|||
*/
|
||||
UnicodeString select(const number::FormattedNumber& number, UErrorCode& status) const;
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Given a formatted number range, returns the overall plural form of the
|
||||
* range. For example, "3-5" returns "other" in English.
|
||||
*
|
||||
* To get a FormattedNumberRange, see NumberRangeFormatter.
|
||||
*
|
||||
* This method only works if PluralRules was created with a locale. If it was created
|
||||
* from PluralRules::createRules(), this method sets status code U_UNSUPPORTED_ERROR.
|
||||
*
|
||||
* @param range The number range onto which the rules will be applied.
|
||||
* @param status Set if an error occurs while selecting plural keyword.
|
||||
* This could happen if the FormattedNumberRange is invalid,
|
||||
* or if plural ranges data is unavailable.
|
||||
* @return The keyword of the selected rule.
|
||||
* @draft ICU 68
|
||||
*/
|
||||
UnicodeString select(const number::FormattedNumberRange& range, UErrorCode& status) const;
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
* @internal
|
||||
*/
|
||||
UnicodeString select(const IFixedDecimal &number) const;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
UnicodeString select(const number::impl::UFormattedNumberRangeData* urange, UErrorCode& status) const;
|
||||
#endif /* U_HIDE_INTERNAL_API */
|
||||
|
||||
/**
|
||||
|
@ -513,12 +542,14 @@ public:
|
|||
|
||||
private:
|
||||
RuleChain *mRules;
|
||||
StandardPluralRanges *mStandardPluralRanges;
|
||||
|
||||
PluralRules(); // default constructor not implemented
|
||||
void parseDescription(const UnicodeString& ruleData, UErrorCode &status);
|
||||
int32_t getNumberValue(const UnicodeString& token) const;
|
||||
UnicodeString getRuleFromResource(const Locale& locale, UPluralType type, UErrorCode& status);
|
||||
RuleChain *rulesForKeyword(const UnicodeString &keyword) const;
|
||||
PluralRules *clone(UErrorCode& status) const;
|
||||
|
||||
/**
|
||||
* An internal status variable used to indicate that the object is in an 'invalid' state.
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
// Forward-declaration
|
||||
struct UFormattedNumber;
|
||||
struct UFormattedNumberRange;
|
||||
|
||||
/**
|
||||
* \file
|
||||
|
@ -167,7 +168,7 @@ uplrules_select(const UPluralRules *uplrules,
|
|||
* @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.
|
||||
* applies to the 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.
|
||||
|
@ -179,6 +180,29 @@ uplrules_selectFormatted(const UPluralRules *uplrules,
|
|||
UChar *keyword, int32_t capacity,
|
||||
UErrorCode *status);
|
||||
|
||||
#ifndef U_HIDE_DRAFT_API
|
||||
/**
|
||||
* Given a formatted number range, returns the overall plural form of the
|
||||
* range. For example, "3-5" returns "other" in English.
|
||||
*
|
||||
* To get a UFormattedNumberRange, see UNumberRangeFormatter.
|
||||
*
|
||||
* @param uplrules The UPluralRules object specifying the rules.
|
||||
* @param urange The number range onto which the rules will be applied.
|
||||
* @param keyword The destination buffer for the keyword of the rule that
|
||||
* applies to the number range.
|
||||
* @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 68
|
||||
*/
|
||||
U_CAPI int32_t U_EXPORT2
|
||||
uplrules_selectForRange(const UPluralRules *uplrules,
|
||||
const struct UFormattedNumberRange* urange,
|
||||
UChar *keyword, int32_t capacity,
|
||||
UErrorCode *status);
|
||||
#endif // U_HIDE_DRAFT_API
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/**
|
||||
* Given a number, returns the keyword of the first rule that applies to the
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "unicode/unumberformatter.h"
|
||||
#include "number_decimalquantity.h"
|
||||
#include "number_utypes.h"
|
||||
#include "numrange_impl.h"
|
||||
|
||||
U_NAMESPACE_USE
|
||||
|
||||
|
@ -115,6 +116,25 @@ uplrules_selectFormatted(const UPluralRules *uplrules,
|
|||
return result.extract(keyword, capacity, *status);
|
||||
}
|
||||
|
||||
U_CAPI int32_t U_EXPORT2
|
||||
uplrules_selectForRange(const UPluralRules *uplrules,
|
||||
const UFormattedNumberRange* urange,
|
||||
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::UFormattedNumberRangeData* impl =
|
||||
number::impl::validateUFormattedNumberRange(urange, *status);
|
||||
UnicodeString result = ((PluralRules*)uplrules)->select(impl, *status);
|
||||
return result.extract(keyword, capacity, *status);
|
||||
}
|
||||
|
||||
U_CAPI int32_t U_EXPORT2
|
||||
uplrules_selectWithFormat(const UPluralRules *uplrules,
|
||||
double number,
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "unicode/ustring.h"
|
||||
#include "unicode/uenum.h"
|
||||
#include "unicode/unumberformatter.h"
|
||||
#include "unicode/unumberrangeformatter.h"
|
||||
#include "cintltst.h"
|
||||
#include "cmemory.h"
|
||||
#include "cstring.h"
|
||||
|
@ -22,6 +23,7 @@ static void TestPluralRules(void);
|
|||
static void TestOrdinalRules(void);
|
||||
static void TestGetKeywords(void);
|
||||
static void TestFormatted(void);
|
||||
static void TestSelectRange(void);
|
||||
|
||||
void addPluralRulesTest(TestNode** root);
|
||||
|
||||
|
@ -33,6 +35,7 @@ void addPluralRulesTest(TestNode** root)
|
|||
TESTCASE(TestOrdinalRules);
|
||||
TESTCASE(TestGetKeywords);
|
||||
TESTCASE(TestFormatted);
|
||||
TESTCASE(TestSelectRange);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
@ -295,4 +298,56 @@ cleanup:
|
|||
unumf_closeResult(uresult);
|
||||
}
|
||||
|
||||
static void TestSelectRange() {
|
||||
UErrorCode ec = U_ZERO_ERROR;
|
||||
UNumberRangeFormatter* unumrf = NULL;
|
||||
UFormattedNumberRange* uresult = NULL;
|
||||
UPluralRules* uplrules = NULL;
|
||||
|
||||
int32_t d1 = 102;
|
||||
int32_t d2 = 201;
|
||||
|
||||
// Locale sl has interesting data: one + two => few
|
||||
uplrules = uplrules_open("sl", &ec);
|
||||
if (!assertSuccess("open plural rules", &ec)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
unumrf = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
|
||||
u"",
|
||||
0,
|
||||
UNUM_RANGE_COLLAPSE_AUTO,
|
||||
UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
|
||||
"sl",
|
||||
NULL,
|
||||
&ec);
|
||||
if (!assertSuccess("open unumrf", &ec)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
uresult = unumrf_openResult(&ec);
|
||||
if (!assertSuccess("open result", &ec)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
unumrf_formatDoubleRange(unumrf, d1, d2, uresult, &ec);
|
||||
if (!assertSuccess("format", &ec)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
UChar buffer[40];
|
||||
int32_t len = uplrules_selectForRange(uplrules, uresult, buffer, 40, &ec);
|
||||
if (!assertSuccess("select", &ec)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
assertUEquals("102-201 is plural category 'few' in sl", u"few", buffer);
|
||||
assertIntEquals("Length should be as expected", u_strlen(buffer), len);
|
||||
|
||||
cleanup:
|
||||
uplrules_close(uplrules);
|
||||
unumrf_close(unumrf);
|
||||
unumrf_closeResult(uresult);
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -974,7 +974,7 @@ group: number_representation
|
|||
group: number_output
|
||||
# PluralRules and FormattedNumber
|
||||
number_output.o
|
||||
standardplural.o plurrule.o
|
||||
standardplural.o plurrule.o pluralranges.o
|
||||
deps
|
||||
# FormattedNumber internals:
|
||||
number_representation format formatted_value_sbimpl units
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "unicode/plurrule.h"
|
||||
#include "unicode/stringpiece.h"
|
||||
#include "unicode/numberformatter.h"
|
||||
#include "unicode/numberrangeformatter.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(testCompactDecimalPluralKeyword);
|
||||
TESTCASE_AUTO(testOrdinal);
|
||||
TESTCASE_AUTO(testSelect);
|
||||
TESTCASE_AUTO(testSelectRange);
|
||||
TESTCASE_AUTO(testAvailbleLocales);
|
||||
TESTCASE_AUTO(testParseErrors);
|
||||
TESTCASE_AUTO(testFixedDecimal);
|
||||
|
@ -925,6 +927,50 @@ void PluralRulesTest::testSelect() {
|
|||
}
|
||||
|
||||
|
||||
void PluralRulesTest::testSelectRange() {
|
||||
IcuTestErrorCode status(*this, "testSelectRange");
|
||||
|
||||
int32_t d1 = 102;
|
||||
int32_t d2 = 201;
|
||||
Locale locale("sl");
|
||||
|
||||
// Locale sl has interesting data: one + two => few
|
||||
auto range = NumberRangeFormatter::withLocale(locale).formatFormattableRange(d1, d2, status);
|
||||
auto rules = LocalPointer<PluralRules>(PluralRules::forLocale(locale, status), status);
|
||||
if (status.errIfFailureAndReset()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For testing: get plural form of first and second numbers
|
||||
auto a = NumberFormatter::withLocale(locale).formatDouble(d1, status);
|
||||
auto b = NumberFormatter::withLocale(locale).formatDouble(d2, status);
|
||||
assertEquals("First plural", u"two", rules->select(a, status));
|
||||
assertEquals("Second plural", u"one", rules->select(b, status));
|
||||
|
||||
// Check the range plural now:
|
||||
auto form = rules->select(range, status);
|
||||
assertEquals("Range plural", u"few", form);
|
||||
|
||||
// Test after copying:
|
||||
PluralRules copy(*rules);
|
||||
form = copy.select(range, status);
|
||||
assertEquals("Range plural after copying", u"few", form);
|
||||
|
||||
// Test when plural ranges data is unavailable:
|
||||
auto bare = LocalPointer<PluralRules>(
|
||||
PluralRules::createRules(u"a: i = 0,1", status), status);
|
||||
if (status.errIfFailureAndReset()) { return; }
|
||||
form = bare->select(range, status);
|
||||
status.expectErrorAndReset(U_UNSUPPORTED_ERROR);
|
||||
|
||||
// However, they should not set an error when no data is available for a language.
|
||||
auto xyz = LocalPointer<PluralRules>(
|
||||
PluralRules::forLocale("xyz", status));
|
||||
form = xyz->select(range, status);
|
||||
assertEquals("Fallback form", u"other", form);
|
||||
}
|
||||
|
||||
|
||||
void PluralRulesTest::testAvailbleLocales() {
|
||||
|
||||
// Hash set of (char *) strings.
|
||||
|
|
|
@ -35,6 +35,7 @@ private:
|
|||
void testCompactDecimalPluralKeyword();
|
||||
void testOrdinal();
|
||||
void testSelect();
|
||||
void testSelectRange();
|
||||
void testAvailbleLocales();
|
||||
void testParseErrors();
|
||||
void testFixedDecimal();
|
||||
|
|
|
@ -18,7 +18,7 @@ import java.util.MissingResourceException;
|
|||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.ibm.icu.text.PluralRanges;
|
||||
import com.ibm.icu.impl.number.range.StandardPluralRanges;
|
||||
import com.ibm.icu.text.PluralRules;
|
||||
import com.ibm.icu.text.PluralRules.PluralType;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
@ -28,19 +28,19 @@ import com.ibm.icu.util.UResourceBundle;
|
|||
* Loader for plural rules data.
|
||||
*/
|
||||
public class PluralRulesLoader extends PluralRules.Factory {
|
||||
private final Map<String, PluralRules> rulesIdToRules;
|
||||
// Key is rules set + ranges set
|
||||
private final Map<String, PluralRules> pluralRulesCache;
|
||||
// lazy init, use getLocaleIdToRulesIdMap to access
|
||||
private Map<String, String> localeIdToCardinalRulesId;
|
||||
private Map<String, String> localeIdToOrdinalRulesId;
|
||||
private Map<String, ULocale> rulesIdToEquivalentULocale;
|
||||
private static Map<String, PluralRanges> localeIdToPluralRanges;
|
||||
|
||||
|
||||
/**
|
||||
* Access through singleton.
|
||||
*/
|
||||
private PluralRulesLoader() {
|
||||
rulesIdToRules = new HashMap<String, PluralRules>();
|
||||
pluralRulesCache = new HashMap<String, PluralRules>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -179,14 +179,20 @@ public class PluralRulesLoader extends PluralRules.Factory {
|
|||
* Gets the rule from the rulesId. If there is no rule for this rulesId,
|
||||
* return null.
|
||||
*/
|
||||
public PluralRules getRulesForRulesId(String rulesId) {
|
||||
public PluralRules getOrCreateRulesForLocale(ULocale locale, PluralRules.PluralType type) {
|
||||
String rulesId = getRulesIdForLocale(locale, type);
|
||||
if (rulesId == null || rulesId.trim().length() == 0) {
|
||||
return null;
|
||||
}
|
||||
String rangesId = StandardPluralRanges.getSetForLocale(locale);
|
||||
String cacheKey = rulesId + "/" + rangesId; // could end with "/null" (this is OK)
|
||||
// synchronize on the map. release the lock temporarily while we build the rules.
|
||||
PluralRules rules = null;
|
||||
boolean hasRules; // Separate boolean because stored rules can be null.
|
||||
synchronized (rulesIdToRules) {
|
||||
hasRules = rulesIdToRules.containsKey(rulesId);
|
||||
synchronized (pluralRulesCache) {
|
||||
hasRules = pluralRulesCache.containsKey(cacheKey);
|
||||
if (hasRules) {
|
||||
rules = rulesIdToRules.get(rulesId); // can be null
|
||||
rules = pluralRulesCache.get(cacheKey); // can be null
|
||||
}
|
||||
}
|
||||
if (!hasRules) {
|
||||
|
@ -205,15 +211,16 @@ public class PluralRulesLoader extends PluralRules.Factory {
|
|||
sb.append(": ");
|
||||
sb.append(b.getString());
|
||||
}
|
||||
rules = PluralRules.parseDescription(sb.toString());
|
||||
StandardPluralRanges ranges = StandardPluralRanges.forSet(rangesId);
|
||||
rules = PluralRules.newInternal(sb.toString(), ranges);
|
||||
} catch (ParseException e) {
|
||||
} catch (MissingResourceException e) {
|
||||
}
|
||||
synchronized (rulesIdToRules) {
|
||||
if (rulesIdToRules.containsKey(rulesId)) {
|
||||
rules = rulesIdToRules.get(rulesId);
|
||||
synchronized (pluralRulesCache) {
|
||||
if (pluralRulesCache.containsKey(cacheKey)) {
|
||||
rules = pluralRulesCache.get(cacheKey);
|
||||
} else {
|
||||
rulesIdToRules.put(rulesId, rules); // can be null
|
||||
pluralRulesCache.put(cacheKey, rules); // can be null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -235,11 +242,7 @@ public class PluralRulesLoader extends PluralRules.Factory {
|
|||
* com.ibm.icu.text.PluralRules.DEFAULT is returned.
|
||||
*/
|
||||
public PluralRules forLocale(ULocale locale, PluralRules.PluralType type) {
|
||||
String rulesId = getRulesIdForLocale(locale, type);
|
||||
if (rulesId == null || rulesId.trim().length() == 0) {
|
||||
return PluralRules.DEFAULT;
|
||||
}
|
||||
PluralRules rules = getRulesForRulesId(rulesId);
|
||||
PluralRules rules = getOrCreateRulesForLocale(locale, type);
|
||||
if (rules == null) {
|
||||
rules = PluralRules.DEFAULT;
|
||||
}
|
||||
|
@ -258,245 +261,4 @@ public class PluralRulesLoader extends PluralRules.Factory {
|
|||
public boolean hasOverride(ULocale locale) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final PluralRanges UNKNOWN_RANGE = new PluralRanges().freeze();
|
||||
|
||||
public PluralRanges getPluralRanges(ULocale locale) {
|
||||
// TODO markdavis Fix the bad fallback, here and elsewhere in this file.
|
||||
String localeId = ULocale.canonicalize(locale.getBaseName());
|
||||
PluralRanges result;
|
||||
while (null == (result = localeIdToPluralRanges.get(localeId))) {
|
||||
int ix = localeId.lastIndexOf("_");
|
||||
if (ix == -1) {
|
||||
result = UNKNOWN_RANGE;
|
||||
break;
|
||||
}
|
||||
localeId = localeId.substring(0, ix);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isPluralRangesAvailable(ULocale locale) {
|
||||
return getPluralRanges(locale) == UNKNOWN_RANGE;
|
||||
}
|
||||
|
||||
// TODO markdavis FIX HARD-CODED HACK once we have data from CLDR in the bundles
|
||||
static {
|
||||
String[][] pluralRangeData = {
|
||||
{"locales", "id ja km ko lo ms my th vi zh"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "am bn fr gu hi hy kn mr pa zu"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "fa"},
|
||||
{"one", "one", "other"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "ka"},
|
||||
{"one", "other", "one"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "az de el gl hu it kk ky ml mn ne nl pt sq sw ta te tr ug uz"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "af bg ca en es et eu fi nb sv ur"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "da fil is"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "si"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "mk"},
|
||||
{"one", "one", "other"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "lv"},
|
||||
{"zero", "zero", "other"},
|
||||
{"zero", "one", "one"},
|
||||
{"zero", "other", "other"},
|
||||
{"one", "zero", "other"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "other", "other"},
|
||||
{"other", "zero", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "ro"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "other", "other"},
|
||||
{"few", "one", "few"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "other", "other"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "hr sr bs"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "other", "other"},
|
||||
{"few", "one", "one"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "sl"},
|
||||
{"one", "one", "few"},
|
||||
{"one", "two", "two"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "other", "other"},
|
||||
{"two", "one", "few"},
|
||||
{"two", "two", "two"},
|
||||
{"two", "few", "few"},
|
||||
{"two", "other", "other"},
|
||||
{"few", "one", "few"},
|
||||
{"few", "two", "two"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "other", "other"},
|
||||
{"other", "one", "few"},
|
||||
{"other", "two", "two"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "he"},
|
||||
{"one", "two", "other"},
|
||||
{"one", "many", "many"},
|
||||
{"one", "other", "other"},
|
||||
{"two", "many", "other"},
|
||||
{"two", "other", "other"},
|
||||
{"many", "many", "many"},
|
||||
{"many", "other", "many"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "two", "other"},
|
||||
{"other", "many", "many"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "cs pl sk"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "many", "many"},
|
||||
{"one", "other", "other"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "many", "many"},
|
||||
{"few", "other", "other"},
|
||||
{"many", "one", "one"},
|
||||
{"many", "few", "few"},
|
||||
{"many", "many", "many"},
|
||||
{"many", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "many", "many"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "lt ru uk"},
|
||||
{"one", "one", "one"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "many", "many"},
|
||||
{"one", "other", "other"},
|
||||
{"few", "one", "one"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "many", "many"},
|
||||
{"few", "other", "other"},
|
||||
{"many", "one", "one"},
|
||||
{"many", "few", "few"},
|
||||
{"many", "many", "many"},
|
||||
{"many", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "many", "many"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "cy"},
|
||||
{"zero", "one", "one"},
|
||||
{"zero", "two", "two"},
|
||||
{"zero", "few", "few"},
|
||||
{"zero", "many", "many"},
|
||||
{"zero", "other", "other"},
|
||||
{"one", "two", "two"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "many", "many"},
|
||||
{"one", "other", "other"},
|
||||
{"two", "few", "few"},
|
||||
{"two", "many", "many"},
|
||||
{"two", "other", "other"},
|
||||
{"few", "many", "many"},
|
||||
{"few", "other", "other"},
|
||||
{"many", "other", "other"},
|
||||
{"other", "one", "one"},
|
||||
{"other", "two", "two"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "many", "many"},
|
||||
{"other", "other", "other"},
|
||||
|
||||
{"locales", "ar"},
|
||||
{"zero", "one", "zero"},
|
||||
{"zero", "two", "zero"},
|
||||
{"zero", "few", "few"},
|
||||
{"zero", "many", "many"},
|
||||
{"zero", "other", "other"},
|
||||
{"one", "two", "other"},
|
||||
{"one", "few", "few"},
|
||||
{"one", "many", "many"},
|
||||
{"one", "other", "other"},
|
||||
{"two", "few", "few"},
|
||||
{"two", "many", "many"},
|
||||
{"two", "other", "other"},
|
||||
{"few", "few", "few"},
|
||||
{"few", "many", "many"},
|
||||
{"few", "other", "other"},
|
||||
{"many", "few", "few"},
|
||||
{"many", "many", "many"},
|
||||
{"many", "other", "other"},
|
||||
{"other", "one", "other"},
|
||||
{"other", "two", "other"},
|
||||
{"other", "few", "few"},
|
||||
{"other", "many", "many"},
|
||||
{"other", "other", "other"},
|
||||
};
|
||||
PluralRanges pr = null;
|
||||
String[] locales = null;
|
||||
HashMap<String, PluralRanges> tempLocaleIdToPluralRanges = new HashMap<String, PluralRanges>();
|
||||
for (String[] row : pluralRangeData) {
|
||||
if (row[0].equals("locales")) {
|
||||
if (pr != null) {
|
||||
pr.freeze();
|
||||
for (String locale : locales) {
|
||||
tempLocaleIdToPluralRanges.put(locale, pr);
|
||||
}
|
||||
}
|
||||
locales = row[1].split(" ");
|
||||
pr = new PluralRanges();
|
||||
} else {
|
||||
pr.add(
|
||||
StandardPlural.fromString(row[0]),
|
||||
StandardPlural.fromString(row[1]),
|
||||
StandardPlural.fromString(row[2]));
|
||||
}
|
||||
}
|
||||
// do last one
|
||||
for (String locale : locales) {
|
||||
tempLocaleIdToPluralRanges.put(locale, pr);
|
||||
}
|
||||
// now make whole thing immutable
|
||||
localeIdToPluralRanges = Collections.unmodifiableMap(tempLocaleIdToPluralRanges);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
package com.ibm.icu.impl.number.range;
|
||||
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.ibm.icu.impl.ICUData;
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
|
@ -10,6 +12,7 @@ import com.ibm.icu.impl.StandardPlural;
|
|||
import com.ibm.icu.impl.UResource;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
import com.ibm.icu.util.UResourceTypeMismatchException;
|
||||
|
||||
/**
|
||||
* @author sffc
|
||||
|
@ -20,8 +23,54 @@ public class StandardPluralRanges {
|
|||
StandardPlural[] flatTriples;
|
||||
int numTriples = 0;
|
||||
|
||||
/**
|
||||
* An immutable map from language codes to set IDs.
|
||||
* Pre-computed and cached in Java since it is used as a cache key for PluralRules.
|
||||
*/
|
||||
private static volatile Map<String, String> languageToSet;
|
||||
|
||||
/** An empty StandardPluralRanges instance. */
|
||||
public static final StandardPluralRanges DEFAULT = new StandardPluralRanges();
|
||||
|
||||
////////////////////
|
||||
|
||||
private static final class PluralRangeSetsDataSink extends UResource.Sink {
|
||||
|
||||
Map<String, String> output;
|
||||
|
||||
PluralRangeSetsDataSink(Map<String, String> output) {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
|
||||
UResource.Table table = value.getTable();
|
||||
for (int i = 0; table.getKeyAndValue(i, key, value); ++i) {
|
||||
// The data has only languages; no regions/scripts. If this changes, this
|
||||
// code and languageToSet will need to change.
|
||||
assert key.toString().equals(new ULocale(key.toString()).getLanguage());
|
||||
output.put(key.toString(), value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, String> getLanguageToSet() {
|
||||
Map<String, String> candidate = languageToSet;
|
||||
if (candidate == null) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
PluralRangeSetsDataSink sink = new PluralRangeSetsDataSink(map);
|
||||
ICUResourceBundle resource = (ICUResourceBundle)
|
||||
UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "pluralRanges");
|
||||
resource.getAllItemsWithFallback("locales", sink);
|
||||
candidate = Collections.unmodifiableMap(map);
|
||||
}
|
||||
// Check if another thread set languageToSet in the mean time
|
||||
if (languageToSet == null) {
|
||||
languageToSet = candidate;
|
||||
}
|
||||
return languageToSet;
|
||||
}
|
||||
|
||||
private static final class PluralRangesDataSink extends UResource.Sink {
|
||||
|
||||
StandardPluralRanges output;
|
||||
|
@ -36,6 +85,10 @@ public class StandardPluralRanges {
|
|||
output.setCapacity(entriesArray.getSize());
|
||||
for (int i = 0; entriesArray.getValue(i, value); ++i) {
|
||||
UResource.Array pluralFormsArray = value.getArray();
|
||||
if (pluralFormsArray.getSize() != 3) {
|
||||
throw new UResourceTypeMismatchException(
|
||||
"Expected 3 elements in pluralRanges.txt array");
|
||||
}
|
||||
pluralFormsArray.getValue(0, value);
|
||||
StandardPlural first = StandardPlural.fromString(value.getString());
|
||||
pluralFormsArray.getValue(1, value);
|
||||
|
@ -48,34 +101,43 @@ public class StandardPluralRanges {
|
|||
}
|
||||
|
||||
private static void getPluralRangesData(
|
||||
ULocale locale,
|
||||
String set,
|
||||
StandardPluralRanges out) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ICUResourceBundle resource;
|
||||
resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "pluralRanges");
|
||||
sb.append("locales/");
|
||||
sb.append(locale.getLanguage());
|
||||
String key = sb.toString();
|
||||
String set;
|
||||
try {
|
||||
set = resource.getStringWithFallback(key);
|
||||
} catch (MissingResourceException e) {
|
||||
// Not all languages are covered: fail gracefully
|
||||
return;
|
||||
}
|
||||
|
||||
sb.setLength(0);
|
||||
sb.append("rules/");
|
||||
sb.append(set);
|
||||
key = sb.toString();
|
||||
String key = sb.toString();
|
||||
PluralRangesDataSink sink = new PluralRangesDataSink(out);
|
||||
resource.getAllItemsWithFallback(key, sink);
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
public StandardPluralRanges(ULocale locale) {
|
||||
getPluralRangesData(locale, this);
|
||||
/** Create a StandardPluralRanges based on locale. */
|
||||
public static StandardPluralRanges forLocale(ULocale locale) {
|
||||
return forSet(getSetForLocale(locale));
|
||||
}
|
||||
|
||||
/** Create a StandardPluralRanges based on set name. */
|
||||
public static StandardPluralRanges forSet(String set) {
|
||||
StandardPluralRanges result = new StandardPluralRanges();
|
||||
if (set == null) {
|
||||
// Not all languages are covered: fail gracefully
|
||||
return DEFAULT;
|
||||
}
|
||||
getPluralRangesData(set, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Get the set name from the locale. */
|
||||
public static String getSetForLocale(ULocale locale) {
|
||||
return getLanguageToSet().get(locale.getLanguage());
|
||||
}
|
||||
|
||||
private StandardPluralRanges() {
|
||||
}
|
||||
|
||||
/** Used for data loading. */
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.ibm.icu.impl.number.DecimalQuantity;
|
|||
import com.ibm.icu.number.NumberRangeFormatter.RangeIdentityResult;
|
||||
import com.ibm.icu.text.ConstrainedFieldPosition;
|
||||
import com.ibm.icu.text.FormattedValue;
|
||||
import com.ibm.icu.text.PluralRules.IFixedDecimal;
|
||||
import com.ibm.icu.util.ICUUncheckedIOException;
|
||||
|
||||
/**
|
||||
|
@ -189,4 +190,22 @@ public class FormattedNumberRange implements FormattedValue {
|
|||
&& quantity1.toBigDecimal().equals(_other.quantity1.toBigDecimal())
|
||||
&& quantity2.toBigDecimal().equals(_other.quantity2.toBigDecimal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public IFixedDecimal getFirstFixedDecimal() {
|
||||
return quantity1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public IFixedDecimal getSecondFixedDecimal() {
|
||||
return quantity2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ class NumberRangeFormatterImpl {
|
|||
getNumberRangeData(macros.loc, nsName, this);
|
||||
|
||||
// TODO: Get locale from PluralRules instead?
|
||||
fPluralRanges = new StandardPluralRanges(macros.loc);
|
||||
fPluralRanges = StandardPluralRanges.forLocale(macros.loc);
|
||||
}
|
||||
|
||||
public FormattedNumberRange format(DecimalQuantity quantity1, DecimalQuantity quantity2, boolean equalBeforeRounding) {
|
||||
|
|
|
@ -1,366 +0,0 @@
|
|||
// © 2016 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2008-2015, Google, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.util.Freezable;
|
||||
import com.ibm.icu.util.Output;
|
||||
|
||||
/**
|
||||
* Utility class for returning the plural category for a range of numbers, such as 1–5, so that appropriate messages can
|
||||
* be chosen. The rules for determining this value vary widely across locales.
|
||||
*
|
||||
* @author markdavis
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class PluralRanges implements Freezable<PluralRanges>, Comparable<PluralRanges> {
|
||||
|
||||
private volatile boolean isFrozen;
|
||||
private Matrix matrix = new Matrix();
|
||||
private boolean[] explicit = new boolean[StandardPlural.COUNT];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public PluralRanges() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class for mapping from two StandardPluralCategories values to another.
|
||||
*/
|
||||
private static final class Matrix implements Comparable<Matrix>, Cloneable {
|
||||
private byte[] data = new byte[StandardPlural.COUNT * StandardPlural.COUNT];
|
||||
{
|
||||
for (int i = 0; i < data.length; ++i) {
|
||||
data[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
Matrix() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for setting.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
void set(StandardPlural start, StandardPlural end, StandardPlural result) {
|
||||
data[start.ordinal() * StandardPlural.COUNT + end.ordinal()] = result == null ? (byte) -1
|
||||
: (byte) result.ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for setting; throws exception if already set.
|
||||
*/
|
||||
void setIfNew(StandardPlural start, StandardPlural end,
|
||||
StandardPlural result) {
|
||||
byte old = data[start.ordinal() * StandardPlural.COUNT + end.ordinal()];
|
||||
if (old >= 0) {
|
||||
throw new IllegalArgumentException("Previously set value for <" + start + ", " + end + ", "
|
||||
+ StandardPlural.VALUES.get(old) + ">");
|
||||
}
|
||||
data[start.ordinal() * StandardPlural.COUNT + end.ordinal()] = result == null ? (byte) -1
|
||||
: (byte) result.ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for getting.
|
||||
*/
|
||||
StandardPlural get(StandardPlural start, StandardPlural end) {
|
||||
byte result = data[start.ordinal() * StandardPlural.COUNT + end.ordinal()];
|
||||
return result < 0 ? null : StandardPlural.VALUES.get(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to see if <*,end> values are all the same.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
StandardPlural endSame(StandardPlural end) {
|
||||
StandardPlural first = null;
|
||||
for (StandardPlural start : StandardPlural.VALUES) {
|
||||
StandardPlural item = get(start, end);
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
if (first == null) {
|
||||
first = item;
|
||||
continue;
|
||||
}
|
||||
if (first != item) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to see if <start,*> values are all the same.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
StandardPlural startSame(StandardPlural start,
|
||||
EnumSet<StandardPlural> endDone, Output<Boolean> emit) {
|
||||
emit.value = false;
|
||||
StandardPlural first = null;
|
||||
for (StandardPlural end : StandardPlural.VALUES) {
|
||||
StandardPlural item = get(start, end);
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
if (first == null) {
|
||||
first = item;
|
||||
continue;
|
||||
}
|
||||
if (first != item) {
|
||||
return null;
|
||||
}
|
||||
// only emit if we didn't cover with the 'end' values
|
||||
if (!endDone.contains(end)) {
|
||||
emit.value = true;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = 0;
|
||||
for (int i = 0; i < data.length; ++i) {
|
||||
result = result * 37 + data[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Matrix)) {
|
||||
return false;
|
||||
}
|
||||
return 0 == compareTo((Matrix) other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Matrix o) {
|
||||
for (int i = 0; i < data.length; ++i) {
|
||||
int diff = data[i] - o.data[i];
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Matrix clone() {
|
||||
Matrix result = new Matrix();
|
||||
result.data = data.clone();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (StandardPlural i : StandardPlural.values()) {
|
||||
for (StandardPlural j : StandardPlural.values()) {
|
||||
StandardPlural x = get(i, j);
|
||||
if (x != null) {
|
||||
result.append(i + " & " + j + " → " + x + ";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for building. If the start or end are null, it means everything of that type.
|
||||
*
|
||||
* @param rangeStart
|
||||
* plural category for the start of the range
|
||||
* @param rangeEnd
|
||||
* plural category for the end of the range
|
||||
* @param result
|
||||
* the resulting plural category
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public void add(StandardPlural rangeStart, StandardPlural rangeEnd,
|
||||
StandardPlural result) {
|
||||
if (isFrozen) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
explicit[result.ordinal()] = true;
|
||||
if (rangeStart == null) {
|
||||
for (StandardPlural rs : StandardPlural.values()) {
|
||||
if (rangeEnd == null) {
|
||||
for (StandardPlural re : StandardPlural.values()) {
|
||||
matrix.setIfNew(rs, re, result);
|
||||
}
|
||||
} else {
|
||||
explicit[rangeEnd.ordinal()] = true;
|
||||
matrix.setIfNew(rs, rangeEnd, result);
|
||||
}
|
||||
}
|
||||
} else if (rangeEnd == null) {
|
||||
explicit[rangeStart.ordinal()] = true;
|
||||
for (StandardPlural re : StandardPlural.values()) {
|
||||
matrix.setIfNew(rangeStart, re, result);
|
||||
}
|
||||
} else {
|
||||
explicit[rangeStart.ordinal()] = true;
|
||||
explicit[rangeEnd.ordinal()] = true;
|
||||
matrix.setIfNew(rangeStart, rangeEnd, result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate plural category for a range from start to end. If there is no available data, then
|
||||
* 'end' is returned as an implicit value. (Such an implicit value can be tested for with {@link #isExplicit}.)
|
||||
*
|
||||
* @param start
|
||||
* plural category for the start of the range
|
||||
* @param end
|
||||
* plural category for the end of the range
|
||||
* @return the resulting plural category, or 'end' if there is no data.
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public StandardPlural get(StandardPlural start, StandardPlural end) {
|
||||
StandardPlural result = matrix.get(start, end);
|
||||
return result == null ? end : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the appropriate plural category for a range from start to end
|
||||
* is explicitly in the data (vs given an implicit value). See also {@link #get}.
|
||||
*
|
||||
* @param start
|
||||
* plural category for the start of the range
|
||||
* @param end
|
||||
* plural category for the end of the range
|
||||
* @return whether the value for (start,end) is explicit or not.
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isExplicit(StandardPlural start, StandardPlural end) {
|
||||
return matrix.get(start, end) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to determines whether the StandardPluralCategories was explicitly used in any add statement.
|
||||
*
|
||||
* @param count
|
||||
* plural category to test
|
||||
* @return true if set
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
public boolean isExplicitlySet(StandardPlural count) {
|
||||
return explicit[count.ordinal()];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof PluralRanges)) {
|
||||
return false;
|
||||
}
|
||||
PluralRanges otherPR = (PluralRanges)other;
|
||||
return matrix.equals(otherPR.matrix) && Arrays.equals(explicit, otherPR.explicit);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public int hashCode() {
|
||||
return matrix.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public int compareTo(PluralRanges that) {
|
||||
return matrix.compareTo(that.matrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean isFrozen() {
|
||||
return isFrozen;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public PluralRanges freeze() {
|
||||
isFrozen = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public PluralRanges cloneAsThawed() {
|
||||
PluralRanges result = new PluralRanges();
|
||||
result.explicit = explicit.clone();
|
||||
result.matrix = matrix.clone();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public String toString() {
|
||||
return matrix.toString();
|
||||
}
|
||||
}
|
|
@ -29,7 +29,10 @@ 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.range.StandardPluralRanges;
|
||||
import com.ibm.icu.number.FormattedNumber;
|
||||
import com.ibm.icu.number.FormattedNumberRange;
|
||||
import com.ibm.icu.number.NumberFormatter;
|
||||
import com.ibm.icu.util.Output;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
@ -181,6 +184,7 @@ public class PluralRules implements Serializable {
|
|||
|
||||
private final RuleList rules;
|
||||
private final transient Set<String> keywords;
|
||||
private final transient StandardPluralRanges standardPluralRanges;
|
||||
|
||||
/**
|
||||
* Provides a factory for returning plural rules
|
||||
|
@ -377,9 +381,7 @@ public class PluralRules implements Serializable {
|
|||
*/
|
||||
public static PluralRules parseDescription(String description)
|
||||
throws ParseException {
|
||||
|
||||
description = description.trim();
|
||||
return description.length() == 0 ? DEFAULT : new PluralRules(parseRuleChain(description));
|
||||
return newInternal(description, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -397,12 +399,25 @@ public class PluralRules implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public static PluralRules newInternal(String description, StandardPluralRanges ranges)
|
||||
throws ParseException {
|
||||
description = description.trim();
|
||||
return description.length() == 0
|
||||
? DEFAULT
|
||||
: new PluralRules(parseRuleChain(description), ranges);
|
||||
}
|
||||
|
||||
/**
|
||||
* The default rules that accept any number and return
|
||||
* {@link #KEYWORD_OTHER}.
|
||||
* @stable ICU 3.8
|
||||
*/
|
||||
public static final PluralRules DEFAULT = new PluralRules(new RuleList().addRule(DEFAULT_RULE));
|
||||
public static final PluralRules DEFAULT = new PluralRules(
|
||||
new RuleList().addRule(DEFAULT_RULE), StandardPluralRanges.DEFAULT);
|
||||
|
||||
/**
|
||||
* @internal CLDR
|
||||
|
@ -2016,9 +2031,10 @@ public class PluralRules implements Serializable {
|
|||
/*
|
||||
* Creates a new <code>PluralRules</code> object. Immutable.
|
||||
*/
|
||||
private PluralRules(RuleList rules) {
|
||||
private PluralRules(RuleList rules, StandardPluralRanges standardPluralRanges) {
|
||||
this.rules = rules;
|
||||
this.keywords = Collections.unmodifiableSet(rules.getKeywords());
|
||||
this.standardPluralRanges = standardPluralRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2058,6 +2074,34 @@ public class PluralRules implements Serializable {
|
|||
return rules.select(number.getFixedDecimal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a formatted number range, returns the overall plural form of the
|
||||
* range. For example, "3-5" returns "other" in English.
|
||||
*
|
||||
* To get a FormattedNumberRange, see {@link com.ibm.icu.number.NumberRangeFormatter}.
|
||||
*
|
||||
* This method only works if PluralRules was created with a locale. If it was created
|
||||
* from PluralRules.createRules(), or if it was deserialized, this method throws
|
||||
* UnsupportedOperationException.
|
||||
*
|
||||
* @param range The number range onto which the rules will be applied.
|
||||
* @return The keyword of the selected rule.
|
||||
* @throws UnsupportedOperationException If called on an instance without plural ranges data.
|
||||
* @draft ICU 68
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public String select(FormattedNumberRange range) {
|
||||
if (standardPluralRanges == null) {
|
||||
throw new UnsupportedOperationException("Plural ranges are unavailable on this instance");
|
||||
}
|
||||
StandardPlural form1 = StandardPlural.fromString(
|
||||
select(range.getFirstFixedDecimal()));
|
||||
StandardPlural form2 = StandardPlural.fromString(
|
||||
select(range.getSecondFixedDecimal()));
|
||||
StandardPlural result = standardPluralRanges.resolve(form1, form2);
|
||||
return result.getKeyword();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a number, returns the keyword of the first rule that applies to
|
||||
* the number.
|
||||
|
|
|
@ -10,7 +10,6 @@ package com.ibm.icu.dev.test.format;
|
|||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
@ -18,12 +17,14 @@ import org.junit.runners.JUnit4;
|
|||
import com.ibm.icu.dev.test.TestFmwk;
|
||||
import com.ibm.icu.impl.SimpleFormatterImpl;
|
||||
import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.impl.number.range.StandardPluralRanges;
|
||||
import com.ibm.icu.number.FormattedNumberRange;
|
||||
import com.ibm.icu.number.NumberFormatter;
|
||||
import com.ibm.icu.number.NumberFormatter.UnitWidth;
|
||||
import com.ibm.icu.number.NumberRangeFormatter;
|
||||
import com.ibm.icu.text.MeasureFormat;
|
||||
import com.ibm.icu.text.MeasureFormat.FormatWidth;
|
||||
import com.ibm.icu.text.PluralRanges;
|
||||
import com.ibm.icu.text.PluralRules.Factory;
|
||||
import com.ibm.icu.util.Currency;
|
||||
import com.ibm.icu.util.Measure;
|
||||
import com.ibm.icu.util.MeasureUnit;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
|
@ -37,7 +38,7 @@ public class PluralRangesTest extends TestFmwk {
|
|||
public void TestLocaleData() {
|
||||
String[][] tests = {
|
||||
{"de", "other", "one", "one"},
|
||||
{"xxx", "few", "few", "few" },
|
||||
{"xxx", "other", "other", "other" },
|
||||
{"de", "one", "other", "other"},
|
||||
{"de", "other", "one", "one"},
|
||||
{"de", "other", "other", "other"},
|
||||
|
@ -50,9 +51,9 @@ public class PluralRangesTest extends TestFmwk {
|
|||
final StandardPlural start = StandardPlural.fromString(test[1]);
|
||||
final StandardPlural end = StandardPlural.fromString(test[2]);
|
||||
final StandardPlural expected = StandardPlural.fromString(test[3]);
|
||||
final PluralRanges pluralRanges = Factory.getDefaultFactory().getPluralRanges(locale);
|
||||
final StandardPluralRanges pluralRanges = StandardPluralRanges.forLocale(locale);
|
||||
|
||||
StandardPlural actual = pluralRanges.get(start, end);
|
||||
StandardPlural actual = pluralRanges.resolve(start, end);
|
||||
assertEquals("Deriving range category", expected, actual);
|
||||
}
|
||||
}
|
||||
|
@ -73,30 +74,28 @@ public class PluralRangesTest extends TestFmwk {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Re-enable this test when #12454 is fixed.
|
||||
@Ignore("http://bugs.icu-project.org/trac/ticket/12454")
|
||||
@Test
|
||||
public void TestFormatting() {
|
||||
Object[][] tests = {
|
||||
{0.0, 1.0, ULocale.FRANCE, FormatWidth.WIDE, MeasureUnit.FAHRENHEIT, "0–1 degré Fahrenheit"},
|
||||
{1.0, 2.0, ULocale.FRANCE, FormatWidth.WIDE, MeasureUnit.FAHRENHEIT, "1–2 degrés Fahrenheit"},
|
||||
{3.1, 4.25, ULocale.FRANCE, FormatWidth.SHORT, MeasureUnit.FAHRENHEIT, "3,1–4,25 °F"},
|
||||
{3.1, 4.25, ULocale.ENGLISH, FormatWidth.SHORT, MeasureUnit.FAHRENHEIT, "3.1–4.25°F"},
|
||||
{3.1, 4.25, ULocale.CHINESE, FormatWidth.WIDE, MeasureUnit.INCH, "3.1-4.25英寸"},
|
||||
{0.0, 1.0, ULocale.ENGLISH, FormatWidth.WIDE, MeasureUnit.INCH, "0–1 inches"},
|
||||
{0.0, 1.0, ULocale.FRANCE, UnitWidth.FULL_NAME, MeasureUnit.FAHRENHEIT, "0–1\u00A0degré Fahrenheit"},
|
||||
{1.0, 2.0, ULocale.FRANCE, UnitWidth.FULL_NAME, MeasureUnit.FAHRENHEIT, "1–2\u00A0degrés Fahrenheit"},
|
||||
{3.1, 4.25, ULocale.FRANCE, UnitWidth.SHORT, MeasureUnit.FAHRENHEIT, "3,1–4,25\u202F°F"},
|
||||
{3.1, 4.25, ULocale.ENGLISH, UnitWidth.SHORT, MeasureUnit.FAHRENHEIT, "3.1–4.25°F"},
|
||||
{3.1, 4.25, ULocale.CHINESE, UnitWidth.FULL_NAME, MeasureUnit.INCH, "3.1-4.25英寸"},
|
||||
{0.0, 1.0, ULocale.ENGLISH, UnitWidth.FULL_NAME, MeasureUnit.INCH, "0–1 inches"},
|
||||
|
||||
{0.0, 1.0, ULocale.ENGLISH, FormatWidth.NARROW, Currency.getInstance("EUR"), "€0.00–1.00"},
|
||||
{0.0, 1.0, ULocale.FRENCH, FormatWidth.NARROW, Currency.getInstance("EUR"), "0,00–1,00 €"},
|
||||
{0.0, 100.0, ULocale.FRENCH, FormatWidth.NARROW, Currency.getInstance("JPY"), "0–100\u00a0JPY"},
|
||||
{0.0, 1.0, ULocale.ENGLISH, UnitWidth.NARROW, Currency.getInstance("EUR"), "€0.00 – €1.00"},
|
||||
{0.0, 1.0, ULocale.FRENCH, UnitWidth.NARROW, Currency.getInstance("EUR"), "0,00–1,00 €"},
|
||||
{0.0, 100.0, ULocale.FRENCH, UnitWidth.NARROW, Currency.getInstance("JPY"), "0–100\u00a0¥"},
|
||||
|
||||
{0.0, 1.0, ULocale.ENGLISH, FormatWidth.SHORT, Currency.getInstance("EUR"), "EUR0.00–1.00"},
|
||||
{0.0, 1.0, ULocale.FRENCH, FormatWidth.SHORT, Currency.getInstance("EUR"), "0,00–1,00\u00a0EUR"},
|
||||
{0.0, 100.0, ULocale.FRENCH, FormatWidth.SHORT, Currency.getInstance("JPY"), "0–100\u00a0JPY"},
|
||||
{0.0, 1.0, ULocale.ENGLISH, UnitWidth.SHORT, Currency.getInstance("EUR"), "€0.00 – €1.00"},
|
||||
{0.0, 1.0, ULocale.FRENCH, UnitWidth.SHORT, Currency.getInstance("EUR"), "0,00–1,00\u00a0€"},
|
||||
{0.0, 100.0, ULocale.FRENCH, UnitWidth.SHORT, Currency.getInstance("JPY"), "0–100\u00a0JPY"},
|
||||
|
||||
{0.0, 1.0, ULocale.ENGLISH, FormatWidth.WIDE, Currency.getInstance("EUR"), "0.00–1.00 euros"},
|
||||
{0.0, 1.0, ULocale.FRENCH, FormatWidth.WIDE, Currency.getInstance("EUR"), "0,00–1,00 euro"},
|
||||
{0.0, 2.0, ULocale.FRENCH, FormatWidth.WIDE, Currency.getInstance("EUR"), "0,00–2,00 euros"},
|
||||
{0.0, 100.0, ULocale.FRENCH, FormatWidth.WIDE, Currency.getInstance("JPY"), "0–100 yens japonais"},
|
||||
{0.0, 1.0, ULocale.ENGLISH, UnitWidth.FULL_NAME, Currency.getInstance("EUR"), "0.00–1.00 euros"},
|
||||
{0.0, 1.0, ULocale.FRENCH, UnitWidth.FULL_NAME, Currency.getInstance("EUR"), "0,00–1,00 euro"},
|
||||
{0.0, 2.0, ULocale.FRENCH, UnitWidth.FULL_NAME, Currency.getInstance("EUR"), "0,00–2,00 euros"},
|
||||
{0.0, 100.0, ULocale.FRENCH, UnitWidth.FULL_NAME, Currency.getInstance("JPY"), "0–100 yens japonais"},
|
||||
};
|
||||
int i = 0;
|
||||
for (Object[] test : tests) {
|
||||
|
@ -104,34 +103,15 @@ public class PluralRangesTest extends TestFmwk {
|
|||
double low = (Double) test[0];
|
||||
double high = (Double) test[1];
|
||||
final ULocale locale = (ULocale) test[2];
|
||||
final FormatWidth width = (FormatWidth) test[3];
|
||||
final UnitWidth unitWidth = (UnitWidth) test[3];
|
||||
final MeasureUnit unit = (MeasureUnit) test[4];
|
||||
final Object expected = test[5];
|
||||
final String expected = (String) test[5];
|
||||
|
||||
MeasureFormat mf = MeasureFormat.getInstance(locale, width);
|
||||
Object actual;
|
||||
try {
|
||||
// TODO: Fix this when range formatting is added again.
|
||||
// To let the code compile, the following line does list formatting.
|
||||
actual = mf.formatMeasures(new Measure(low, unit), new Measure(high, unit));
|
||||
} catch (Exception e) {
|
||||
actual = e.getClass();
|
||||
}
|
||||
assertEquals(i + " Formatting unit", expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestBasic() {
|
||||
PluralRanges a = new PluralRanges();
|
||||
a.add(StandardPlural.ONE, StandardPlural.OTHER, StandardPlural.ONE);
|
||||
StandardPlural actual = a.get(StandardPlural.ONE, StandardPlural.OTHER);
|
||||
assertEquals("range", StandardPlural.ONE, actual);
|
||||
a.freeze();
|
||||
try {
|
||||
a.add(StandardPlural.ONE, StandardPlural.ONE, StandardPlural.ONE);
|
||||
errln("Failed to cause exception on frozen instance");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
FormattedNumberRange actual = NumberRangeFormatter.with()
|
||||
.numberFormatterBoth(NumberFormatter.with().unit(unit).unitWidth(unitWidth))
|
||||
.locale(locale)
|
||||
.formatRange(low, high);
|
||||
assertEquals(i + " Formatting unit", expected, actual.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,10 @@ 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.FormattedNumberRange;
|
||||
import com.ibm.icu.number.LocalizedNumberFormatter;
|
||||
import com.ibm.icu.number.NumberFormatter;
|
||||
import com.ibm.icu.number.NumberRangeFormatter;
|
||||
import com.ibm.icu.number.Precision;
|
||||
import com.ibm.icu.number.UnlocalizedNumberFormatter;
|
||||
import com.ibm.icu.text.NumberFormat;
|
||||
|
@ -1279,4 +1281,37 @@ public class PluralRulesTest extends TestFmwk {
|
|||
Locale.setDefault(Locale.GERMAN);
|
||||
assertEquals("FixedDecimal toString", expected, fd.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectRange() {
|
||||
int d1 = 102;
|
||||
int d2 = 201;
|
||||
ULocale locale = new ULocale("sl");
|
||||
|
||||
// Locale sl has interesting data: one + two => few
|
||||
FormattedNumberRange range = NumberRangeFormatter.withLocale(locale).formatRange(d1, d2);
|
||||
PluralRules rules = PluralRules.forLocale(locale);
|
||||
|
||||
// For testing: get plural form of first and second numbers
|
||||
FormattedNumber a = NumberFormatter.withLocale(locale).format(d1);
|
||||
FormattedNumber b = NumberFormatter.withLocale(locale).format(d2);
|
||||
assertEquals("First plural", "two", rules.select(a));
|
||||
assertEquals("Second plural", "one", rules.select(b));
|
||||
|
||||
// Check the range plural now:
|
||||
String form = rules.select(range);
|
||||
assertEquals("Range plural", "few", form);
|
||||
|
||||
// Test when plural ranges data is unavailable:
|
||||
PluralRules bare = PluralRules.createRules("a: i = 0,1");
|
||||
try {
|
||||
form = bare.select(range);
|
||||
fail("Expected exception");
|
||||
} catch (UnsupportedOperationException e) {}
|
||||
|
||||
// However, they should not throw when no data is available for a language.
|
||||
PluralRules xyz = PluralRules.forLocale(new ULocale("xyz"));
|
||||
form = xyz.select(range);
|
||||
assertEquals("Fallback form", "other", form);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue