ICU-11276 Adding ModifierStore, groundwork for plural range support.
This commit is contained in:
parent
5c43434285
commit
3161453c02
@ -53,6 +53,14 @@ void U_CALLCONV initDefaultCurrencySpacing(UErrorCode &status) {
|
||||
|
||||
Modifier::~Modifier() = default;
|
||||
|
||||
ModifierStore::~ModifierStore() = default;
|
||||
|
||||
AdoptingModifierStore::~AdoptingModifierStore() {
|
||||
for (const Modifier *mod : mods) {
|
||||
delete mod;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32_t ConstantAffixModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex,
|
||||
UErrorCode &status) const {
|
||||
@ -81,7 +89,13 @@ bool ConstantAffixModifier::containsField(UNumberFormatFields field) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConstantAffixModifier::operator==(const Modifier& other) const {
|
||||
void ConstantAffixModifier::getParameters(Parameters& output) const {
|
||||
(void)output;
|
||||
// This method is not currently used.
|
||||
U_ASSERT(false);
|
||||
}
|
||||
|
||||
bool ConstantAffixModifier::semanticallyEquivalent(const Modifier& other) const {
|
||||
auto* _other = dynamic_cast<const ConstantAffixModifier*>(&other);
|
||||
if (_other == nullptr) {
|
||||
return false;
|
||||
@ -160,7 +174,13 @@ bool SimpleModifier::containsField(UNumberFormatFields field) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SimpleModifier::operator==(const Modifier& other) const {
|
||||
void SimpleModifier::getParameters(Parameters& output) const {
|
||||
(void)output;
|
||||
// This method is not currently used.
|
||||
U_ASSERT(false);
|
||||
}
|
||||
|
||||
bool SimpleModifier::semanticallyEquivalent(const Modifier& other) const {
|
||||
auto* _other = dynamic_cast<const SimpleModifier*>(&other);
|
||||
if (_other == nullptr) {
|
||||
return false;
|
||||
@ -283,7 +303,13 @@ bool ConstantMultiFieldModifier::containsField(UNumberFormatFields field) const
|
||||
return fPrefix.containsField(field) || fSuffix.containsField(field);
|
||||
}
|
||||
|
||||
bool ConstantMultiFieldModifier::operator==(const Modifier& other) const {
|
||||
void ConstantMultiFieldModifier::getParameters(Parameters& output) const {
|
||||
(void)output;
|
||||
// This method is not currently used.
|
||||
U_ASSERT(false);
|
||||
}
|
||||
|
||||
bool ConstantMultiFieldModifier::semanticallyEquivalent(const Modifier& other) const {
|
||||
auto* _other = dynamic_cast<const ConstantMultiFieldModifier*>(&other);
|
||||
if (_other == nullptr) {
|
||||
return false;
|
||||
|
@ -39,7 +39,9 @@ class U_I18N_API ConstantAffixModifier : public Modifier, public UObject {
|
||||
|
||||
bool containsField(UNumberFormatFields field) const U_OVERRIDE;
|
||||
|
||||
bool operator==(const Modifier& other) const U_OVERRIDE;
|
||||
void getParameters(Parameters& output) const U_OVERRIDE;
|
||||
|
||||
bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE;
|
||||
|
||||
private:
|
||||
UnicodeString fPrefix;
|
||||
@ -70,7 +72,9 @@ class U_I18N_API SimpleModifier : public Modifier, public UMemory {
|
||||
|
||||
bool containsField(UNumberFormatFields field) const U_OVERRIDE;
|
||||
|
||||
bool operator==(const Modifier& other) const U_OVERRIDE;
|
||||
void getParameters(Parameters& output) const U_OVERRIDE;
|
||||
|
||||
bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE;
|
||||
|
||||
/**
|
||||
* TODO: This belongs in SimpleFormatterImpl. The only reason I haven't moved it there yet is because
|
||||
@ -148,7 +152,9 @@ class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory {
|
||||
|
||||
bool containsField(UNumberFormatFields field) const U_OVERRIDE;
|
||||
|
||||
bool operator==(const Modifier& other) const U_OVERRIDE;
|
||||
void getParameters(Parameters& output) const U_OVERRIDE;
|
||||
|
||||
bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE;
|
||||
|
||||
protected:
|
||||
// NOTE: In Java, these are stored as array pointers. In C++, the NumberStringBuilder is stored by
|
||||
@ -237,7 +243,11 @@ class U_I18N_API EmptyModifier : public Modifier, public UMemory {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==(const Modifier& other) const U_OVERRIDE {
|
||||
void getParameters(Parameters& output) const U_OVERRIDE {
|
||||
output.obj = nullptr;
|
||||
}
|
||||
|
||||
bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE {
|
||||
return other.getCodePointCount() == 0;
|
||||
}
|
||||
|
||||
@ -246,50 +256,55 @@ class U_I18N_API EmptyModifier : public Modifier, public UMemory {
|
||||
};
|
||||
|
||||
/**
|
||||
* A ParameterizedModifier by itself is NOT a Modifier. Rather, it wraps a data structure containing two or more
|
||||
* Modifiers and returns the modifier appropriate for the current situation.
|
||||
* This implementation of ModifierStore adopts Modifer pointers.
|
||||
*/
|
||||
class U_I18N_API ParameterizedModifier : public UMemory {
|
||||
class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory {
|
||||
public:
|
||||
// NOTE: mods is zero-initialized (to nullptr)
|
||||
ParameterizedModifier() : mods() {
|
||||
}
|
||||
virtual ~AdoptingModifierStore();
|
||||
|
||||
static constexpr StandardPlural::Form DEFAULT_STANDARD_PLURAL = StandardPlural::OTHER;
|
||||
|
||||
AdoptingModifierStore() = default;
|
||||
|
||||
// No copying!
|
||||
ParameterizedModifier(const ParameterizedModifier &other) = delete;
|
||||
AdoptingModifierStore(const AdoptingModifierStore &other) = delete;
|
||||
|
||||
~ParameterizedModifier() {
|
||||
for (const Modifier *mod : mods) {
|
||||
delete mod;
|
||||
}
|
||||
}
|
||||
|
||||
void adoptPositiveNegativeModifiers(
|
||||
const Modifier *positive, const Modifier *zero, const Modifier *negative) {
|
||||
mods[2] = positive;
|
||||
mods[1] = zero;
|
||||
mods[0] = negative;
|
||||
}
|
||||
|
||||
/** The modifier is ADOPTED. */
|
||||
void adoptSignPluralModifier(int8_t signum, StandardPlural::Form plural, const Modifier *mod) {
|
||||
/**
|
||||
* Sets the Modifier with the specified signum and plural form.
|
||||
*/
|
||||
void adoptModifier(int8_t signum, StandardPlural::Form plural, const Modifier *mod) {
|
||||
mods[getModIndex(signum, plural)] = mod;
|
||||
}
|
||||
|
||||
/** Returns a reference to the modifier; no ownership change. */
|
||||
const Modifier *getModifier(int8_t signum) const {
|
||||
return mods[signum + 1];
|
||||
/**
|
||||
* Sets the Modifier with the specified signum.
|
||||
* The modifier will apply to all plural forms.
|
||||
*/
|
||||
void adoptModifierWithoutPlural(int8_t signum, const Modifier *mod) {
|
||||
mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] = mod;
|
||||
}
|
||||
|
||||
/** Returns a reference to the modifier; no ownership change. */
|
||||
const Modifier *getModifier(int8_t signum, StandardPlural::Form plural) const {
|
||||
return mods[getModIndex(signum, plural)];
|
||||
const Modifier *getModifier(int8_t signum, StandardPlural::Form plural) const U_OVERRIDE {
|
||||
const Modifier* modifier = mods[getModIndex(signum, plural)];
|
||||
if (modifier == nullptr && plural != DEFAULT_STANDARD_PLURAL) {
|
||||
modifier = mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)];
|
||||
}
|
||||
return modifier;
|
||||
}
|
||||
|
||||
/** Returns a reference to the modifier; no ownership change. */
|
||||
const Modifier *getModifierWithoutPlural(int8_t signum) const {
|
||||
return mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)];
|
||||
}
|
||||
|
||||
private:
|
||||
const Modifier *mods[3 * StandardPlural::COUNT];
|
||||
// NOTE: mods is zero-initialized (to nullptr)
|
||||
const Modifier *mods[3 * StandardPlural::COUNT] = {};
|
||||
|
||||
inline static int32_t getModIndex(int8_t signum, StandardPlural::Form plural) {
|
||||
U_ASSERT(signum >= -1 && signum <= 1);
|
||||
U_ASSERT(plural >= 0 && plural < StandardPlural::COUNT);
|
||||
return static_cast<int32_t>(plural) * 3 + (signum + 1);
|
||||
}
|
||||
};
|
||||
|
@ -69,7 +69,7 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren
|
||||
StandardPlural::Form::MANY,
|
||||
StandardPlural::Form::OTHER};
|
||||
|
||||
auto pm = new ParameterizedModifier();
|
||||
auto pm = new AdoptingModifierStore();
|
||||
if (pm == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
@ -79,11 +79,11 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren
|
||||
// Slower path when we require the plural keyword.
|
||||
for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
|
||||
setNumberProperties(1, plural);
|
||||
pm->adoptSignPluralModifier(1, plural, createConstantModifier(status));
|
||||
pm->adoptModifier(1, plural, createConstantModifier(status));
|
||||
setNumberProperties(0, plural);
|
||||
pm->adoptSignPluralModifier(0, plural, createConstantModifier(status));
|
||||
pm->adoptModifier(0, plural, createConstantModifier(status));
|
||||
setNumberProperties(-1, plural);
|
||||
pm->adoptSignPluralModifier(-1, plural, createConstantModifier(status));
|
||||
pm->adoptModifier(-1, plural, createConstantModifier(status));
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
delete pm;
|
||||
@ -93,12 +93,11 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren
|
||||
} else {
|
||||
// Faster path when plural keyword is not needed.
|
||||
setNumberProperties(1, StandardPlural::Form::COUNT);
|
||||
Modifier* positive = createConstantModifier(status);
|
||||
pm->adoptModifierWithoutPlural(1, createConstantModifier(status));
|
||||
setNumberProperties(0, StandardPlural::Form::COUNT);
|
||||
Modifier* zero = createConstantModifier(status);
|
||||
pm->adoptModifierWithoutPlural(0, createConstantModifier(status));
|
||||
setNumberProperties(-1, StandardPlural::Form::COUNT);
|
||||
Modifier* negative = createConstantModifier(status);
|
||||
pm->adoptPositiveNegativeModifiers(positive, zero, negative);
|
||||
pm->adoptModifierWithoutPlural(-1, createConstantModifier(status));
|
||||
if (U_FAILURE(status)) {
|
||||
delete pm;
|
||||
return nullptr;
|
||||
@ -120,7 +119,7 @@ ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErro
|
||||
}
|
||||
}
|
||||
|
||||
ImmutablePatternModifier::ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules,
|
||||
ImmutablePatternModifier::ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules,
|
||||
const MicroPropsGenerator* parent)
|
||||
: pm(pm), rules(rules), parent(parent) {}
|
||||
|
||||
@ -132,7 +131,7 @@ void ImmutablePatternModifier::processQuantity(DecimalQuantity& quantity, MicroP
|
||||
|
||||
void ImmutablePatternModifier::applyToMicros(MicroProps& micros, DecimalQuantity& quantity) const {
|
||||
if (rules == nullptr) {
|
||||
micros.modMiddle = pm->getModifier(quantity.signum());
|
||||
micros.modMiddle = pm->getModifierWithoutPlural(quantity.signum());
|
||||
} else {
|
||||
// TODO: Fix this. Avoid the copy.
|
||||
DecimalQuantity copy(quantity);
|
||||
@ -144,7 +143,7 @@ void ImmutablePatternModifier::applyToMicros(MicroProps& micros, DecimalQuantity
|
||||
|
||||
const Modifier* ImmutablePatternModifier::getModifier(int8_t signum, StandardPlural::Form plural) const {
|
||||
if (rules == nullptr) {
|
||||
return pm->getModifier(signum);
|
||||
return pm->getModifierWithoutPlural(signum);
|
||||
} else {
|
||||
return pm->getModifier(signum, plural);
|
||||
}
|
||||
@ -241,7 +240,13 @@ bool MutablePatternModifier::containsField(UNumberFormatFields field) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MutablePatternModifier::operator==(const Modifier& other) const {
|
||||
void MutablePatternModifier::getParameters(Parameters& output) const {
|
||||
(void)output;
|
||||
// This method is not currently used.
|
||||
U_ASSERT(false);
|
||||
}
|
||||
|
||||
bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const {
|
||||
(void)other;
|
||||
// This method is not currently used.
|
||||
U_ASSERT(false);
|
||||
|
@ -18,13 +18,13 @@
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
// Export an explicit template instantiation of the LocalPointer that is used as a
|
||||
// data member of ParameterizedModifier.
|
||||
// data member of AdoptingModifierStore.
|
||||
// (When building DLLs for Windows this is required.)
|
||||
#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
|
||||
// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!=
|
||||
#pragma warning(suppress: 4661)
|
||||
template class U_I18N_API LocalPointerBase<number::impl::ParameterizedModifier>;
|
||||
template class U_I18N_API LocalPointer<number::impl::ParameterizedModifier>;
|
||||
template class U_I18N_API LocalPointerBase<number::impl::AdoptingModifierStore>;
|
||||
template class U_I18N_API LocalPointer<number::impl::AdoptingModifierStore>;
|
||||
#endif
|
||||
|
||||
namespace number {
|
||||
@ -45,10 +45,10 @@ class U_I18N_API ImmutablePatternModifier : public MicroPropsGenerator, public U
|
||||
const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const;
|
||||
|
||||
private:
|
||||
ImmutablePatternModifier(ParameterizedModifier* pm, const PluralRules* rules,
|
||||
ImmutablePatternModifier(AdoptingModifierStore* pm, const PluralRules* rules,
|
||||
const MicroPropsGenerator* parent);
|
||||
|
||||
const LocalPointer<ParameterizedModifier> pm;
|
||||
const LocalPointer<AdoptingModifierStore> pm;
|
||||
const PluralRules* rules;
|
||||
const MicroPropsGenerator* parent;
|
||||
|
||||
@ -186,7 +186,9 @@ class U_I18N_API MutablePatternModifier
|
||||
|
||||
bool containsField(UNumberFormatFields field) const U_OVERRIDE;
|
||||
|
||||
bool operator==(const Modifier& other) const U_OVERRIDE;
|
||||
void getParameters(Parameters& output) const U_OVERRIDE;
|
||||
|
||||
bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Returns the string that substitutes a given symbol type in a pattern.
|
||||
|
@ -100,7 +100,13 @@ bool ScientificModifier::containsField(UNumberFormatFields field) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScientificModifier::operator==(const Modifier& other) const {
|
||||
void ScientificModifier::getParameters(Parameters& output) const {
|
||||
(void)output;
|
||||
// This method is not used for inner modifiers.
|
||||
U_ASSERT(false);
|
||||
}
|
||||
|
||||
bool ScientificModifier::semanticallyEquivalent(const Modifier& other) const {
|
||||
auto* _other = dynamic_cast<const ScientificModifier*>(&other);
|
||||
if (_other == nullptr) {
|
||||
return false;
|
||||
|
@ -32,7 +32,9 @@ class U_I18N_API ScientificModifier : public UMemory, public Modifier {
|
||||
|
||||
bool containsField(UNumberFormatFields field) const U_OVERRIDE;
|
||||
|
||||
bool operator==(const Modifier& other) const U_OVERRIDE;
|
||||
void getParameters(Parameters& output) const U_OVERRIDE;
|
||||
|
||||
bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE;
|
||||
|
||||
private:
|
||||
int32_t fExponent;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "uassert.h"
|
||||
#include "unicode/platform.h"
|
||||
#include "unicode/uniset.h"
|
||||
#include "standardplural.h"
|
||||
|
||||
U_NAMESPACE_BEGIN namespace number {
|
||||
namespace impl {
|
||||
@ -45,6 +46,7 @@ class Modifier;
|
||||
class MutablePatternModifier;
|
||||
class DecimalQuantity;
|
||||
class NumberStringBuilder;
|
||||
class ModifierStore;
|
||||
struct MicroProps;
|
||||
|
||||
|
||||
@ -133,7 +135,7 @@ class U_I18N_API AffixPatternProvider {
|
||||
* builder. A Modifier usually contains a prefix and a suffix that are applied, but it could contain something else,
|
||||
* like a {@link com.ibm.icu.text.SimpleFormatter} pattern.
|
||||
*
|
||||
* A Modifier is usually immutable, except in cases such as {@link MurkyModifier}, which are mutable for performance
|
||||
* A Modifier is usually immutable, except in cases such as {@link MutablePatternModifier}, which are mutable for performance
|
||||
* reasons.
|
||||
*
|
||||
* Exported as U_I18N_API because it is a base class for other exported types
|
||||
@ -185,11 +187,45 @@ class U_I18N_API Modifier {
|
||||
virtual bool containsField(UNumberFormatFields field) const = 0;
|
||||
|
||||
/**
|
||||
* Returns whether the affixes owned by this modifier are equal to the ones owned by the given modifier.
|
||||
* A fill-in for getParameters(). obj will always be set; if non-null, the other
|
||||
* two fields are also safe to read.
|
||||
*/
|
||||
virtual bool operator==(const Modifier& other) const = 0;
|
||||
struct Parameters {
|
||||
const ModifierStore* obj = nullptr;
|
||||
int8_t signum;
|
||||
StandardPlural::Form plural;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a set of "parameters" for this Modifier.
|
||||
*/
|
||||
virtual void getParameters(Parameters& output) const = 0;
|
||||
|
||||
/**
|
||||
* Returns whether this Modifier is *semantically equivalent* to the other Modifier;
|
||||
* in many cases, this is the same as equal, but parameters should be ignored.
|
||||
*/
|
||||
virtual bool semanticallyEquivalent(const Modifier& other) const = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This is *not* a modifier; rather, it is an object that can return modifiers
|
||||
* based on given parameters.
|
||||
*
|
||||
* Exported as U_I18N_API because it is a base class for other exported types.
|
||||
*/
|
||||
class U_I18N_API ModifierStore {
|
||||
public:
|
||||
virtual ~ModifierStore();
|
||||
|
||||
/**
|
||||
* Returns a Modifier with the given parameters (best-effort).
|
||||
*/
|
||||
virtual const Modifier* getModifier(int8_t signum, StandardPlural::Form plural) const = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This interface is used when all number formatting settings, including the locale, are known, except for the quantity
|
||||
* itself. The {@link #processQuantity} method performs the final step in the number processing pipeline: it uses the
|
||||
|
@ -119,9 +119,9 @@ void NumberRangeFormatterImpl::format(UFormattedNumberRangeData& data, bool equa
|
||||
// TODO: Write this as MicroProps operator==() ?
|
||||
// TODO: Avoid the redundancy of these equality operations with the
|
||||
// ones in formatRange?
|
||||
if (!(*micros1.modInner == *micros2.modInner)
|
||||
|| !(*micros1.modMiddle == *micros2.modMiddle)
|
||||
|| !(*micros1.modOuter == *micros2.modOuter)) {
|
||||
if (!micros1.modInner->semanticallyEquivalent(*micros2.modInner)
|
||||
|| !micros1.modMiddle->semanticallyEquivalent(*micros2.modMiddle)
|
||||
|| !micros1.modOuter->semanticallyEquivalent(*micros2.modOuter)) {
|
||||
formatRange(data, micros1, micros2, status);
|
||||
data.identityResult = UNUM_IDENTITY_RESULT_NOT_EQUAL;
|
||||
return;
|
||||
@ -220,7 +220,7 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
|
||||
case UNUM_RANGE_COLLAPSE_UNIT:
|
||||
{
|
||||
// OUTER MODIFIER
|
||||
collapseOuter = *micros1.modOuter == *micros2.modOuter;
|
||||
collapseOuter = micros1.modOuter->semanticallyEquivalent(*micros2.modOuter);
|
||||
|
||||
if (!collapseOuter) {
|
||||
// Never collapse inner mods if outer mods are not collapsable
|
||||
@ -230,7 +230,7 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
|
||||
}
|
||||
|
||||
// MIDDLE MODIFIER
|
||||
collapseMiddle = *micros1.modMiddle == *micros2.modMiddle;
|
||||
collapseMiddle = micros1.modMiddle->semanticallyEquivalent(*micros2.modMiddle);
|
||||
|
||||
if (!collapseMiddle) {
|
||||
// Never collapse inner mods if outer mods are not collapsable
|
||||
@ -262,7 +262,7 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
|
||||
}
|
||||
|
||||
// INNER MODIFIER
|
||||
collapseInner = *micros1.modInner == *micros2.modInner;
|
||||
collapseInner = micros1.modInner->semanticallyEquivalent(*micros2.modInner);
|
||||
|
||||
// All done checking for collapsability.
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user