ICU-11276 Adding ModifierStore, groundwork for plural range support.

This commit is contained in:
Shane Carr 2018-09-13 22:16:29 -07:00
parent 5c43434285
commit 3161453c02
No known key found for this signature in database
GPG Key ID: FCED3B24AAB18B5C
8 changed files with 156 additions and 64 deletions

View File

@ -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;

View File

@ -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);
}
};

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;