ICU-22093 ICU4C: Add SimpleNumber and SimpleNumberFormatter

See #2241
This commit is contained in:
Shane F. Carr 2022-11-23 22:48:12 +00:00
parent 194236a1b4
commit a2854b615a
32 changed files with 2072 additions and 598 deletions

View file

@ -99,7 +99,7 @@ static const char *gNumberElementKeys[DecimalFormatSymbols::kFormatSymbolCount]
// Initializes this with the decimal format symbols in the default locale.
DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status)
: UObject(), locale(), currPattern(NULL) {
: UObject(), locale() {
initialize(locale, status, true);
}
@ -107,17 +107,17 @@ DecimalFormatSymbols::DecimalFormatSymbols(UErrorCode& status)
// Initializes this with the decimal format symbols in the desired locale.
DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, UErrorCode& status)
: UObject(), locale(loc), currPattern(NULL) {
: UObject(), locale(loc) {
initialize(locale, status);
}
DecimalFormatSymbols::DecimalFormatSymbols(const Locale& loc, const NumberingSystem& ns, UErrorCode& status)
: UObject(), locale(loc), currPattern(NULL) {
: UObject(), locale(loc) {
initialize(locale, status, false, &ns);
}
DecimalFormatSymbols::DecimalFormatSymbols()
: UObject(), locale(Locale::getRoot()), currPattern(NULL) {
: UObject(), locale(Locale::getRoot()) {
*validLocale = *actualLocale = 0;
initialize();
}
@ -169,6 +169,7 @@ DecimalFormatSymbols::operator=(const DecimalFormatSymbols& rhs)
fIsCustomIntlCurrencySymbol = rhs.fIsCustomIntlCurrencySymbol;
fCodePointZero = rhs.fCodePointZero;
currPattern = rhs.currPattern;
uprv_strcpy(nsName, rhs.nsName);
}
return *this;
}
@ -383,6 +384,7 @@ DecimalFormatSymbols::initialize(const Locale& loc, UErrorCode& status,
} else {
nsName = gLatn;
}
uprv_strcpy(this->nsName, nsName);
// Open resource bundles
const char* locStr = loc.getName();
@ -517,7 +519,7 @@ DecimalFormatSymbols::initialize() {
fCodePointZero = 0x30;
U_ASSERT(fCodePointZero == fSymbols[kZeroDigitSymbol].char32At(0));
currPattern = nullptr;
nsName[0] = 0;
}
void DecimalFormatSymbols::setCurrency(const UChar* currency, UErrorCode& status) {

View file

@ -153,6 +153,9 @@ public:
virtual ~FormattedValueStringBuilderImpl();
FormattedValueStringBuilderImpl(FormattedValueStringBuilderImpl&&) = default;
FormattedValueStringBuilderImpl& operator=(FormattedValueStringBuilderImpl&&) = default;
// Implementation of FormattedValue (const):
UnicodeString toString(UErrorCode& status) const U_OVERRIDE;

View file

@ -202,6 +202,7 @@
<ClCompile Include="number_mapper.cpp" />
<ClCompile Include="number_multiplier.cpp" />
<ClCompile Include="number_currencysymbols.cpp" />
<ClCompile Include="number_simple.cpp" />
<ClCompile Include="number_skeletons.cpp" />
<ClCompile Include="number_symbolswrapper.cpp" />
<ClCompile Include="number_capi.cpp" />

View file

@ -609,6 +609,9 @@
<ClCompile Include="number_currencysymbols.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_simple.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_skeletons.cpp">
<Filter>formatting</Filter>
</ClCompile>

View file

@ -435,6 +435,7 @@
<ClCompile Include="number_mapper.cpp" />
<ClCompile Include="number_multiplier.cpp" />
<ClCompile Include="number_currencysymbols.cpp" />
<ClCompile Include="number_simple.cpp" />
<ClCompile Include="number_skeletons.cpp" />
<ClCompile Include="number_symbolswrapper.cpp" />
<ClCompile Include="number_capi.cpp" />

View file

@ -16,6 +16,8 @@
#include "number_decnum.h"
#include "unicode/numberformatter.h"
#include "unicode/unumberformatter.h"
#include "unicode/simplenumberformatter.h"
#include "unicode/usimplenumberformatter.h"
using namespace icu;
using namespace icu::number;
@ -35,6 +37,24 @@ struct UNumberFormatterData : public UMemory,
LocalizedNumberFormatter fFormatter;
};
/**
* Implementation class for USimpleNumber. Wraps a SimpleNumberFormatter.
*/
struct USimpleNumberData : public UMemory,
// Magic number as ASCII == "SNM" (SimpleNuMber)
public IcuCApiHelper<USimpleNumber, USimpleNumberData, 0x534E4D00> {
SimpleNumber fNumber;
};
/**
* Implementation class for USimpleNumberFormatter. Wraps a SimpleNumberFormatter.
*/
struct USimpleNumberFormatterData : public UMemory,
// Magic number as ASCII == "SNF" (SimpleNumberFormatter)
public IcuCApiHelper<USimpleNumberFormatter, USimpleNumberFormatterData, 0x534E4600> {
SimpleNumberFormatter fFormatter;
};
struct UFormattedNumberImpl;
// Magic number as ASCII == "FDN" (FormatteDNumber)
@ -46,6 +66,8 @@ struct UFormattedNumberImpl : public UFormattedValueImpl, public UFormattedNumbe
FormattedNumber fImpl;
UFormattedNumberData fData;
void setTo(FormattedNumber value);
};
UFormattedNumberImpl::UFormattedNumberImpl()
@ -58,6 +80,10 @@ UFormattedNumberImpl::~UFormattedNumberImpl() {
fImpl.fData = nullptr;
}
void UFormattedNumberImpl::setTo(FormattedNumber value) {
fData = std::move(*value.fData);
}
}
}
U_NAMESPACE_END
@ -225,6 +251,144 @@ unumf_close(UNumberFormatter* f) {
}
///// SIMPLE NUMBER FORMATTER /////
U_CAPI USimpleNumber* U_EXPORT2
usnum_openForInt64(int64_t value, UErrorCode* ec) {
auto* impl = new USimpleNumberData();
if (impl == nullptr) {
*ec = U_MEMORY_ALLOCATION_ERROR;
return nullptr;
}
impl->fNumber = SimpleNumber::forInt64(value, *ec);
return impl->exportForC();
}
U_CAPI void U_EXPORT2
usnum_multiplyByPowerOfTen(USimpleNumber* unumber, int32_t power, UErrorCode* ec) {
auto* number = USimpleNumberData::validate(unumber, *ec);
if (U_FAILURE(*ec)) {
return;
}
number->fNumber.multiplyByPowerOfTen(power, *ec);
}
U_CAPI void U_EXPORT2
usnum_roundTo(USimpleNumber* unumber, int32_t position, UNumberFormatRoundingMode roundingMode, UErrorCode* ec) {
auto* number = USimpleNumberData::validate(unumber, *ec);
if (U_FAILURE(*ec)) {
return;
}
number->fNumber.roundTo(position, roundingMode, *ec);
}
U_CAPI void U_EXPORT2
usnum_setMinimumIntegerDigits(USimpleNumber* unumber, int32_t minimumIntegerDigits, UErrorCode* ec) {
auto* number = USimpleNumberData::validate(unumber, *ec);
if (U_FAILURE(*ec)) {
return;
}
number->fNumber.setMinimumIntegerDigits(minimumIntegerDigits, *ec);
}
U_CAPI void U_EXPORT2
usnum_setMinimumFractionDigits(USimpleNumber* unumber, int32_t minimumFractionDigits, UErrorCode* ec) {
auto* number = USimpleNumberData::validate(unumber, *ec);
if (U_FAILURE(*ec)) {
return;
}
number->fNumber.setMinimumFractionDigits(minimumFractionDigits, *ec);
}
U_CAPI void U_EXPORT2
usnum_truncateStart(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec) {
auto* number = USimpleNumberData::validate(unumber, *ec);
if (U_FAILURE(*ec)) {
return;
}
number->fNumber.truncateStart(maximumIntegerDigits, *ec);
}
U_CAPI void U_EXPORT2
usnum_setSign(USimpleNumber* unumber, USimpleNumberSign sign, UErrorCode* ec) {
auto* number = USimpleNumberData::validate(unumber, *ec);
if (U_FAILURE(*ec)) {
return;
}
number->fNumber.setSign(sign, *ec);
}
U_CAPI USimpleNumberFormatter* U_EXPORT2
usnumf_openForLocale(const char* locale, UErrorCode* ec) {
auto* impl = new USimpleNumberFormatterData();
if (impl == nullptr) {
*ec = U_MEMORY_ALLOCATION_ERROR;
return nullptr;
}
impl->fFormatter = SimpleNumberFormatter::forLocale(locale, *ec);
return impl->exportForC();
}
U_CAPI USimpleNumberFormatter* U_EXPORT2
usnumf_openForLocaleAndGroupingStrategy(
const char* locale, UNumberGroupingStrategy groupingStrategy, UErrorCode* ec) {
auto* impl = new USimpleNumberFormatterData();
if (impl == nullptr) {
*ec = U_MEMORY_ALLOCATION_ERROR;
return nullptr;
}
impl->fFormatter = SimpleNumberFormatter::forLocaleAndGroupingStrategy(locale, groupingStrategy, *ec);
return impl->exportForC();
}
U_CAPI void U_EXPORT2
usnumf_formatAndAdoptNumber(
const USimpleNumberFormatter* uformatter,
USimpleNumber* unumber,
UFormattedNumber* uresult,
UErrorCode* ec) {
auto* formatter = USimpleNumberFormatterData::validate(uformatter, *ec);
auto* number = USimpleNumberData::validate(unumber, *ec);
auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
if (U_FAILURE(*ec)) {
delete number;
return;
}
auto localResult = formatter->fFormatter.format(std::move(number->fNumber), *ec);
result->setTo(std::move(localResult));
delete number;
}
U_CAPI void U_EXPORT2
usnumf_formatInt64(
const USimpleNumberFormatter* uformatter,
int64_t value,
UFormattedNumber* uresult,
UErrorCode* ec) {
auto* formatter = USimpleNumberFormatterData::validate(uformatter, *ec);
auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
if (U_FAILURE(*ec)) {
return;
}
auto localResult = formatter->fFormatter.formatInt64(value, *ec);
result->setTo(std::move(localResult));
}
U_CAPI void U_EXPORT2
usnum_close(USimpleNumber* unumber) {
UErrorCode localStatus = U_ZERO_ERROR;
const USimpleNumberData* impl = USimpleNumberData::validate(unumber, localStatus);
delete impl;
}
U_CAPI void U_EXPORT2
usnumf_close(USimpleNumberFormatter* uformatter) {
UErrorCode localStatus = U_ZERO_ERROR;
const USimpleNumberFormatterData* impl = USimpleNumberFormatterData::validate(uformatter, localStatus);
delete impl;
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -36,7 +36,7 @@ int32_t NumberFormatterImpl::formatStatic(const MacroProps &macros, UFormattedNu
NumberFormatterImpl impl(macros, false, status);
MicroProps& micros = impl.preProcessUnsafe(inValue, status);
if (U_FAILURE(status)) { return 0; }
int32_t length = writeNumber(micros, inValue, outString, 0, status);
int32_t length = writeNumber(micros.simple, inValue, outString, 0, status);
length += writeAffixes(micros, outString, 0, length, status);
results->outputUnit = std::move(micros.outputUnit);
results->gender = micros.gender;
@ -61,7 +61,7 @@ int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &s
MicroProps micros;
preProcess(inValue, micros, status);
if (U_FAILURE(status)) { return 0; }
int32_t length = writeNumber(micros, inValue, outString, 0, status);
int32_t length = writeNumber(micros.simple, inValue, outString, 0, status);
length += writeAffixes(micros, outString, 0, length, status);
results->outputUnit = std::move(micros.outputUnit);
results->gender = micros.gender;
@ -186,7 +186,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
// Resolve the symbols. Do this here because currency may need to customize them.
if (macros.symbols.isDecimalFormatSymbols()) {
fMicros.symbols = macros.symbols.getDecimalFormatSymbols();
fMicros.simple.symbols = macros.symbols.getDecimalFormatSymbols();
} else {
LocalPointer<DecimalFormatSymbols> newSymbols(
new DecimalFormatSymbols(macros.locale, *ns, status), status);
@ -199,15 +199,15 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
return nullptr;
}
}
fMicros.symbols = newSymbols.getAlias();
fMicros.simple.symbols = newSymbols.getAlias();
fSymbols.adoptInstead(newSymbols.orphan());
}
// Load and parse the pattern string. It is used for grouping sizes and affixes only.
// If we are formatting currency, check for a currency-specific pattern.
const char16_t* pattern = nullptr;
if (isCurrency && fMicros.symbols->getCurrencyPattern() != nullptr) {
pattern = fMicros.symbols->getCurrencyPattern();
if (isCurrency && fMicros.simple.symbols->getCurrencyPattern() != nullptr) {
pattern = fMicros.simple.symbols->getCurrencyPattern();
}
if (pattern == nullptr) {
CldrPatternStyle patternStyle;
@ -291,14 +291,14 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
// Grouping strategy
if (!macros.grouper.isBogus()) {
fMicros.grouping = macros.grouper;
fMicros.simple.grouping = macros.grouper;
} else if (isCompactNotation) {
// Compact notation uses minGrouping by default since ICU 59
fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2);
fMicros.simple.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2);
} else {
fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO);
fMicros.simple.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO);
}
fMicros.grouping.setLocaleData(*fPatternInfo, macros.locale);
fMicros.simple.grouping.setLocaleData(*fPatternInfo, macros.locale);
// Padding strategy
if (!macros.padder.isBogus()) {
@ -323,17 +323,17 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
// Decimal mark display
if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) {
fMicros.decimal = macros.decimal;
fMicros.simple.decimal = macros.decimal;
} else {
fMicros.decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
fMicros.simple.decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
}
// Use monetary separator symbols
fMicros.useCurrency = isCurrency;
fMicros.simple.useCurrency = isCurrency;
// Inner modifier (scientific notation)
if (macros.notation.fType == Notation::NTN_SCIENTIFIC) {
auto newScientificHandler = new ScientificHandler(&macros.notation, fMicros.symbols, chain);
auto newScientificHandler = new ScientificHandler(&macros.notation, fMicros.simple.symbols, chain);
if (newScientificHandler == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return nullptr;
@ -362,13 +362,13 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
patternModifier->setPatternAttributes(fMicros.sign, isPermille, macros.approximately);
if (patternModifier->needsPlurals()) {
patternModifier->setSymbols(
fMicros.symbols,
fMicros.simple.symbols,
currency,
unitWidth,
resolvePluralRules(macros.rules, macros.locale, status),
status);
} else {
patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status);
patternModifier->setSymbols(fMicros.simple.symbols, currency, unitWidth, nullptr, status);
}
if (safe) {
fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status),
@ -380,7 +380,7 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
// currencyAsDecimal
if (affixProvider->currencyAsDecimal()) {
fMicros.currencyAsDecimal = patternModifier->getCurrencySymbolForUnitWidth(status);
fMicros.simple.currencyAsDecimal = patternModifier->getCurrencySymbolForUnitWidth(status);
}
// Outer modifier (CLDR units and currency long names)
@ -481,8 +481,10 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
}
const PluralRules*
NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale,
UErrorCode& status) {
NumberFormatterImpl::resolvePluralRules(
const PluralRules* rulesPtr,
const Locale& locale,
UErrorCode& status) {
if (rulesPtr != nullptr) {
return rulesPtr;
}
@ -493,8 +495,12 @@ NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Local
return fRules.getAlias();
}
int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string,
int32_t start, int32_t end, UErrorCode& status) {
int32_t NumberFormatterImpl::writeAffixes(
const MicroProps& micros,
FormattedStringBuilder& string,
int32_t start,
int32_t end,
UErrorCode& status) {
U_ASSERT(micros.modOuter != nullptr);
// Always apply the inner modifier (which is "strong").
int32_t length = micros.modInner->apply(string, start, end, status);
@ -508,9 +514,12 @@ int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStr
return length;
}
int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity,
FormattedStringBuilder& string, int32_t index,
UErrorCode& status) {
int32_t NumberFormatterImpl::writeNumber(
const SimpleMicroProps& micros,
DecimalQuantity& quantity,
FormattedStringBuilder& string,
int32_t index,
UErrorCode& status) {
int32_t length = 0;
if (quantity.isInfinite()) {
length += string.insert(
@ -528,7 +537,12 @@ int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuanti
} else {
// Add the integer digits
length += writeIntegerDigits(micros, quantity, string, length + index, status);
length += writeIntegerDigits(
micros,
quantity,
string,
length + index,
status);
// Add the decimal point
if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) {
@ -573,9 +587,12 @@ int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuanti
return length;
}
int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity,
FormattedStringBuilder& string, int32_t index,
UErrorCode& status) {
int32_t NumberFormatterImpl::writeIntegerDigits(
const SimpleMicroProps& micros,
DecimalQuantity& quantity,
FormattedStringBuilder& string,
int32_t index,
UErrorCode& status) {
int length = 0;
int integerCount = quantity.getUpperDisplayMagnitude() + 1;
for (int i = 0; i < integerCount; i++) {
@ -605,9 +622,12 @@ int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, Decima
return length;
}
int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity,
FormattedStringBuilder& string, int32_t index,
UErrorCode& status) {
int32_t NumberFormatterImpl::writeFractionDigits(
const SimpleMicroProps& micros,
DecimalQuantity& quantity,
FormattedStringBuilder& string,
int32_t index,
UErrorCode& status) {
int length = 0;
int fractionCount = -quantity.getLowerDisplayMagnitude();
for (int i = 0; i < fractionCount; i++) {

View file

@ -79,14 +79,22 @@ class NumberFormatterImpl : public UMemory {
* Synthesizes the output string from a MicroProps and DecimalQuantity.
* This method formats only the main number, not affixes.
*/
static int32_t writeNumber(const MicroProps& micros, DecimalQuantity& quantity,
FormattedStringBuilder& string, int32_t index, UErrorCode& status);
static int32_t writeNumber(
const SimpleMicroProps& micros,
DecimalQuantity& quantity,
FormattedStringBuilder& string,
int32_t index,
UErrorCode& status);
/**
* Adds the affixes. Intended to be called immediately after formatNumber.
*/
static int32_t writeAffixes(const MicroProps& micros, FormattedStringBuilder& string, int32_t start,
int32_t end, UErrorCode& status);
static int32_t writeAffixes(
const MicroProps& micros,
FormattedStringBuilder& string,
int32_t start,
int32_t end,
UErrorCode& status);
private:
// Head of the MicroPropsGenerator linked list. Subclasses' processQuantity
@ -146,12 +154,20 @@ class NumberFormatterImpl : public UMemory {
macrosToMicroGenerator(const MacroProps &macros, bool safe, UErrorCode &status);
static int32_t
writeIntegerDigits(const MicroProps &micros, DecimalQuantity &quantity, FormattedStringBuilder &string,
int32_t index, UErrorCode &status);
writeIntegerDigits(
const SimpleMicroProps& micros,
DecimalQuantity &quantity,
FormattedStringBuilder &string,
int32_t index,
UErrorCode &status);
static int32_t
writeFractionDigits(const MicroProps &micros, DecimalQuantity &quantity, FormattedStringBuilder &string,
int32_t index, UErrorCode &status);
writeFractionDigits(
const SimpleMicroProps& micros,
DecimalQuantity &quantity,
FormattedStringBuilder &string,
int32_t index,
UErrorCode &status);
};
} // namespace impl

View file

@ -67,33 +67,39 @@ class IntMeasures : public MaybeStackArray<int64_t, 2> {
UErrorCode status = U_ZERO_ERROR;
};
struct SimpleMicroProps : public UMemory {
Grouper grouping;
bool useCurrency = false;
UNumberDecimalSeparatorDisplay decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
// Currency symbol to be used as the decimal separator
UnicodeString currencyAsDecimal = ICU_Utility::makeBogusString();
// Note: This struct has no direct ownership of the following pointer.
const DecimalFormatSymbols* symbols = nullptr;
};
/**
* MicroProps is the first MicroPropsGenerator that should be should be called,
* producing an initialized MicroProps instance that will be passed on and
* modified throughout the rest of the chain of MicroPropsGenerator instances.
*/
struct MicroProps : public MicroPropsGenerator {
SimpleMicroProps simple;
// NOTE: All of these fields are properly initialized in NumberFormatterImpl.
RoundingImpl rounder;
Grouper grouping;
Padder padding;
IntegerWidth integerWidth;
UNumberSignDisplay sign;
UNumberDecimalSeparatorDisplay decimal;
bool useCurrency;
char nsName[9];
// Currency symbol to be used as the decimal separator
UnicodeString currencyAsDecimal = ICU_Utility::makeBogusString();
// No ownership: must point at a string which will outlive MicroProps
// instances, e.g. a string with static storage duration, or just a string
// that will never be deallocated or modified.
const char *gender;
// Note: This struct has no direct ownership of the following pointers.
const DecimalFormatSymbols* symbols;
// Pointers to Modifiers provided by the number formatting pipeline (when
// the value is known):

View file

@ -62,12 +62,21 @@ Modifier::Parameters::Parameters(
ModifierStore::~ModifierStore() = default;
AdoptingModifierStore::~AdoptingModifierStore() {
AdoptingSignumModifierStore::~AdoptingSignumModifierStore() {
for (const Modifier *mod : mods) {
delete mod;
}
}
AdoptingSignumModifierStore&
AdoptingSignumModifierStore::operator=(AdoptingSignumModifierStore&& other) U_NOEXCEPT {
for (size_t i=0; i<SIGNUM_COUNT; i++) {
this->mods[i] = other.mods[i];
other.mods[i] = nullptr;
}
return *this;
}
int32_t ConstantAffixModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex,
UErrorCode &status) const {

View file

@ -272,13 +272,45 @@ class U_I18N_API EmptyModifier : public Modifier, public UMemory {
bool fStrong;
};
/** An adopting Modifier store that varies by signum but not plural form. */
class U_I18N_API AdoptingSignumModifierStore : public UMemory {
public:
virtual ~AdoptingSignumModifierStore();
AdoptingSignumModifierStore() = default;
// No copying!
AdoptingSignumModifierStore(const AdoptingSignumModifierStore &other) = delete;
AdoptingSignumModifierStore& operator=(const AdoptingSignumModifierStore& other) = delete;
// Moving is OK
AdoptingSignumModifierStore(AdoptingSignumModifierStore &&other) U_NOEXCEPT {
*this = std::move(other);
}
AdoptingSignumModifierStore& operator=(AdoptingSignumModifierStore&& other) U_NOEXCEPT;
/** Take ownership of the Modifier and slot it in at the given Signum. */
void adoptModifier(Signum signum, const Modifier* mod) {
U_ASSERT(mods[signum] == nullptr);
mods[signum] = mod;
}
inline const Modifier*& operator[](Signum signum) {
return mods[signum];
}
inline Modifier const* operator[](Signum signum) const {
return mods[signum];
}
private:
const Modifier* mods[SIGNUM_COUNT] = {};
};
/**
* This implementation of ModifierStore adopts Modifier pointers.
*/
class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory {
public:
virtual ~AdoptingModifierStore();
static constexpr StandardPlural::Form DEFAULT_STANDARD_PLURAL = StandardPlural::OTHER;
AdoptingModifierStore() = default;
@ -286,46 +318,36 @@ class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory {
// No copying!
AdoptingModifierStore(const AdoptingModifierStore &other) = delete;
/**
* Sets the Modifier with the specified signum and plural form.
*/
void adoptModifier(Signum signum, StandardPlural::Form plural, const Modifier *mod) {
U_ASSERT(mods[getModIndex(signum, plural)] == nullptr);
mods[getModIndex(signum, plural)] = mod;
// Moving is OK
AdoptingModifierStore(AdoptingModifierStore &&other) = default;
/** Sets the modifiers for a specific plural form. */
void adoptSignumModifierStore(StandardPlural::Form plural, AdoptingSignumModifierStore other) {
mods[plural] = std::move(other);
}
/**
* Sets the Modifier with the specified signum.
* The modifier will apply to all plural forms.
*/
void adoptModifierWithoutPlural(Signum signum, const Modifier *mod) {
U_ASSERT(mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] == nullptr);
mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] = mod;
/** Sets the modifiers for the default plural form. */
void adoptSignumModifierStoreNoPlural(AdoptingSignumModifierStore other) {
mods[DEFAULT_STANDARD_PLURAL] = std::move(other);
}
/** Returns a reference to the modifier; no ownership change. */
const Modifier *getModifier(Signum signum, StandardPlural::Form plural) const U_OVERRIDE {
const Modifier* modifier = mods[getModIndex(signum, plural)];
const Modifier* modifier = mods[plural][signum];
if (modifier == nullptr && plural != DEFAULT_STANDARD_PLURAL) {
modifier = mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)];
modifier = mods[DEFAULT_STANDARD_PLURAL][signum];
}
return modifier;
}
/** Returns a reference to the modifier; no ownership change. */
const Modifier *getModifierWithoutPlural(Signum signum) const {
return mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)];
return mods[DEFAULT_STANDARD_PLURAL][signum];
}
private:
// NOTE: mods is zero-initialized (to nullptr)
const Modifier *mods[4 * StandardPlural::COUNT] = {};
inline static int32_t getModIndex(Signum signum, StandardPlural::Form plural) {
U_ASSERT(signum >= 0 && signum < SIGNUM_COUNT);
U_ASSERT(plural >= 0 && plural < StandardPlural::COUNT);
return static_cast<int32_t>(plural) * SIGNUM_COUNT + signum;
}
AdoptingSignumModifierStore mods[StandardPlural::COUNT] = {};
};
} // namespace impl

View file

@ -60,6 +60,21 @@ bool MutablePatternModifier::needsPlurals() const {
// Silently ignore any error codes.
}
AdoptingSignumModifierStore MutablePatternModifier::createImmutableForPlural(StandardPlural::Form plural, UErrorCode& status) {
AdoptingSignumModifierStore pm;
setNumberProperties(SIGNUM_POS, plural);
pm.adoptModifier(SIGNUM_POS, createConstantModifier(status));
setNumberProperties(SIGNUM_NEG_ZERO, plural);
pm.adoptModifier(SIGNUM_NEG_ZERO, createConstantModifier(status));
setNumberProperties(SIGNUM_POS_ZERO, plural);
pm.adoptModifier(SIGNUM_POS_ZERO, createConstantModifier(status));
setNumberProperties(SIGNUM_NEG, plural);
pm.adoptModifier(SIGNUM_NEG, createConstantModifier(status));
return pm;
}
ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& status) {
// TODO: Move StandardPlural VALUES to standardplural.h
static const StandardPlural::Form STANDARD_PLURAL_VALUES[] = {
@ -79,14 +94,7 @@ ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& st
if (needsPlurals()) {
// Slower path when we require the plural keyword.
for (StandardPlural::Form plural : STANDARD_PLURAL_VALUES) {
setNumberProperties(SIGNUM_POS, plural);
pm->adoptModifier(SIGNUM_POS, plural, createConstantModifier(status));
setNumberProperties(SIGNUM_NEG_ZERO, plural);
pm->adoptModifier(SIGNUM_NEG_ZERO, plural, createConstantModifier(status));
setNumberProperties(SIGNUM_POS_ZERO, plural);
pm->adoptModifier(SIGNUM_POS_ZERO, plural, createConstantModifier(status));
setNumberProperties(SIGNUM_NEG, plural);
pm->adoptModifier(SIGNUM_NEG, plural, createConstantModifier(status));
pm->adoptSignumModifierStore(plural, createImmutableForPlural(plural, status));
}
if (U_FAILURE(status)) {
delete pm;
@ -95,14 +103,7 @@ ImmutablePatternModifier* MutablePatternModifier::createImmutable(UErrorCode& st
return new ImmutablePatternModifier(pm, fRules); // adopts pm
} else {
// Faster path when plural keyword is not needed.
setNumberProperties(SIGNUM_POS, StandardPlural::Form::COUNT);
pm->adoptModifierWithoutPlural(SIGNUM_POS, createConstantModifier(status));
setNumberProperties(SIGNUM_NEG_ZERO, StandardPlural::Form::COUNT);
pm->adoptModifierWithoutPlural(SIGNUM_NEG_ZERO, createConstantModifier(status));
setNumberProperties(SIGNUM_POS_ZERO, StandardPlural::Form::COUNT);
pm->adoptModifierWithoutPlural(SIGNUM_POS_ZERO, createConstantModifier(status));
setNumberProperties(SIGNUM_NEG, StandardPlural::Form::COUNT);
pm->adoptModifierWithoutPlural(SIGNUM_NEG, createConstantModifier(status));
pm->adoptSignumModifierStoreNoPlural(createImmutableForPlural(StandardPlural::Form::COUNT, status));
if (U_FAILURE(status)) {
delete pm;
return nullptr;

View file

@ -156,6 +156,9 @@ class U_I18N_API MutablePatternModifier
*/
bool needsPlurals() const;
/** Creates a quantity-dependent Modifier for the specified plural form. */
AdoptingSignumModifierStore createImmutableForPlural(StandardPlural::Form plural, UErrorCode& status);
/**
* Creates a new quantity-dependent Modifier that behaves the same as the current instance, but which is immutable
* and can be saved for future use. The number properties in the current instance are mutated; all other properties

View file

@ -0,0 +1,255 @@
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/numberformatter.h"
#include "unicode/simplenumberformatter.h"
#include "number_formatimpl.h"
#include "number_utils.h"
#include "number_patternmodifier.h"
#include "number_utypes.h"
using namespace icu;
using namespace icu::number;
using namespace icu::number::impl;
SimpleNumber
SimpleNumber::forInt64(int64_t value, UErrorCode& status) {
if (U_FAILURE(status)) {
return SimpleNumber();
}
auto results = new UFormattedNumberData();
if (results == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return SimpleNumber();
}
results->quantity.setToLong(value);
return SimpleNumber(results, status);
}
SimpleNumber::SimpleNumber(UFormattedNumberData* data, UErrorCode& status) : fData(data) {
if (U_FAILURE(status)) {
return;
}
if (fData == nullptr) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
if (fData->quantity.isNegative()) {
fSign = UNUM_SIMPLE_NUMBER_MINUS_SIGN;
} else {
fSign = UNUM_SIMPLE_NUMBER_NO_SIGN;
}
}
void SimpleNumber::cleanup() {
delete fData;
fData = nullptr;
}
void SimpleNumber::multiplyByPowerOfTen(int32_t power, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
if (fData == nullptr) {
status = U_INVALID_STATE_ERROR;
return;
}
fData->quantity.adjustMagnitude(power);
}
void SimpleNumber::roundTo(int32_t position, UNumberFormatRoundingMode roundingMode, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
if (fData == nullptr) {
status = U_INVALID_STATE_ERROR;
return;
}
fData->quantity.roundToMagnitude(position, roundingMode, status);
}
void SimpleNumber::setMinimumIntegerDigits(uint32_t position, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
if (fData == nullptr) {
status = U_INVALID_STATE_ERROR;
return;
}
fData->quantity.setMinInteger(position);
}
void SimpleNumber::setMinimumFractionDigits(uint32_t position, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
if (fData == nullptr) {
status = U_INVALID_STATE_ERROR;
return;
}
fData->quantity.setMinFraction(position);
}
void SimpleNumber::truncateStart(uint32_t position, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
if (fData == nullptr) {
status = U_INVALID_STATE_ERROR;
return;
}
fData->quantity.applyMaxInteger(position);
}
void SimpleNumber::setSign(USimpleNumberSign sign, UErrorCode& status) {
if (U_FAILURE(status)) {
return;
}
if (fData == nullptr) {
status = U_INVALID_STATE_ERROR;
return;
}
fSign = sign;
}
void SimpleNumberFormatter::cleanup() {
delete fOwnedSymbols;
delete fMicros;
delete fPatternModifier;
fOwnedSymbols = nullptr;
fMicros = nullptr;
fPatternModifier = nullptr;
}
SimpleNumberFormatter SimpleNumberFormatter::forLocale(const icu::Locale &locale, UErrorCode &status) {
return SimpleNumberFormatter::forLocaleAndGroupingStrategy(locale, UNUM_GROUPING_AUTO, status);
}
SimpleNumberFormatter SimpleNumberFormatter::forLocaleAndGroupingStrategy(
const icu::Locale &locale,
UNumberGroupingStrategy groupingStrategy,
UErrorCode &status) {
SimpleNumberFormatter retval;
retval.fOwnedSymbols = new DecimalFormatSymbols(locale, status);
if (U_FAILURE(status)) {
return retval;
}
if (retval.fOwnedSymbols == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return retval;
}
retval.initialize(locale, *retval.fOwnedSymbols, groupingStrategy, status);
return retval;
}
SimpleNumberFormatter SimpleNumberFormatter::forLocaleAndSymbolsAndGroupingStrategy(
const icu::Locale &locale,
const DecimalFormatSymbols &symbols,
UNumberGroupingStrategy groupingStrategy,
UErrorCode &status) {
SimpleNumberFormatter retval;
retval.initialize(locale, symbols, groupingStrategy, status);
return retval;
}
void SimpleNumberFormatter::initialize(
const icu::Locale &locale,
const DecimalFormatSymbols &symbols,
UNumberGroupingStrategy groupingStrategy,
UErrorCode &status) {
if (U_FAILURE(status)) {
return;
}
fMicros = new SimpleMicroProps();
if (fMicros == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
fMicros->symbols = &symbols;
auto pattern = utils::getPatternForStyle(
locale,
symbols.getNumberingSystemName(),
CLDR_PATTERN_STYLE_DECIMAL,
status);
if (U_FAILURE(status)) {
return;
}
ParsedPatternInfo patternInfo;
PatternParser::parseToPatternInfo(UnicodeString(pattern), patternInfo, status);
if (U_FAILURE(status)) {
return;
}
auto grouper = Grouper::forStrategy(groupingStrategy);
grouper.setLocaleData(patternInfo, locale);
fMicros->grouping = grouper;
MutablePatternModifier patternModifier(false);
patternModifier.setPatternInfo(&patternInfo, kUndefinedField);
patternModifier.setPatternAttributes(UNUM_SIGN_EXCEPT_ZERO, false, false);
patternModifier.setSymbols(fMicros->symbols, {}, UNUM_UNIT_WIDTH_SHORT, nullptr, status);
fPatternModifier = new AdoptingSignumModifierStore(patternModifier.createImmutableForPlural(StandardPlural::COUNT, status));
fGroupingStrategy = groupingStrategy;
return;
}
FormattedNumber SimpleNumberFormatter::format(SimpleNumber value, UErrorCode &status) const {
formatImpl(value.fData, value.fSign, status);
// Do not save the results object if we encountered a failure.
if (U_SUCCESS(status)) {
auto temp = value.fData;
value.fData = nullptr;
return FormattedNumber(temp);
} else {
return FormattedNumber(status);
}
}
void SimpleNumberFormatter::formatImpl(UFormattedNumberData* data, USimpleNumberSign sign, UErrorCode &status) const {
if (U_FAILURE(status)) {
return;
}
if (data == nullptr) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
if (fPatternModifier == nullptr || fMicros == nullptr) {
status = U_INVALID_STATE_ERROR;
return;
}
Signum signum;
if (sign == UNUM_SIMPLE_NUMBER_MINUS_SIGN) {
signum = SIGNUM_NEG;
} else if (sign == UNUM_SIMPLE_NUMBER_PLUS_SIGN) {
signum = SIGNUM_POS;
} else {
signum = SIGNUM_POS_ZERO;
}
const Modifier* modifier = (*fPatternModifier)[signum];
auto length = NumberFormatterImpl::writeNumber(
*fMicros,
data->quantity,
data->getStringRef(),
0,
status);
length += modifier->apply(data->getStringRef(), 0, length, status);
data->getStringRef().writeTerminator(status);
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -35,6 +35,9 @@ public:
UFormattedNumberData() : FormattedValueStringBuilderImpl(kUndefinedField) {}
virtual ~UFormattedNumberData();
UFormattedNumberData(UFormattedNumberData&&) = default;
UFormattedNumberData& operator=(UFormattedNumberData&&) = default;
// The formatted quantity.
DecimalQuantity quantity;

View file

@ -239,7 +239,7 @@ void NumberRangeFormatterImpl::formatSingleValue(UFormattedNumberRangeData& data
UErrorCode& status) const {
if (U_FAILURE(status)) { return; }
if (fSameFormatters) {
int32_t length = NumberFormatterImpl::writeNumber(micros1, data.quantity1, data.getStringRef(), 0, status);
int32_t length = NumberFormatterImpl::writeNumber(micros1.simple, data.quantity1, data.getStringRef(), 0, status);
NumberFormatterImpl::writeAffixes(micros1, data.getStringRef(), 0, length, status);
} else {
formatRange(data, micros1, micros2, status);
@ -256,7 +256,7 @@ void NumberRangeFormatterImpl::formatApproximately (UFormattedNumberRangeData& d
MicroProps microsAppx;
data.quantity1.resetExponent();
fApproximatelyFormatter.preProcess(data.quantity1, microsAppx, status);
int32_t length = NumberFormatterImpl::writeNumber(microsAppx, data.quantity1, data.getStringRef(), 0, status);
int32_t length = NumberFormatterImpl::writeNumber(microsAppx.simple, data.quantity1, data.getStringRef(), 0, status);
length += microsAppx.modInner->apply(data.getStringRef(), 0, length, status);
length += microsAppx.modMiddle->apply(data.getStringRef(), 0, length, status);
microsAppx.modOuter->apply(data.getStringRef(), 0, length, status);
@ -384,10 +384,10 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
}
}
length1 += NumberFormatterImpl::writeNumber(micros1, data.quantity1, string, UPRV_INDEX_0, status);
length1 += NumberFormatterImpl::writeNumber(micros1.simple, data.quantity1, string, UPRV_INDEX_0, status);
// ICU-21684: Write the second number to a temp string to avoid repeated insert operations
FormattedStringBuilder tempString;
NumberFormatterImpl::writeNumber(micros2, data.quantity2, tempString, 0, status);
NumberFormatterImpl::writeNumber(micros2.simple, data.quantity2, tempString, 0, status);
length2 += string.insert(UPRV_INDEX_2, tempString, status);
// TODO: Support padding?

View file

@ -122,6 +122,7 @@ number_patternmodifier.cpp
number_patternstring.cpp
number_rounding.cpp
number_scientific.cpp
number_simple.cpp
number_skeletons.cpp
number_symbolswrapper.cpp
number_usageprefs.cpp

View file

@ -456,6 +456,12 @@ public:
* @internal
*/
inline const char16_t* getCurrencyPattern(void) const;
/**
* Returns the numbering system with which this DecimalFormatSymbols was initialized.
* @internal
*/
inline const char* getNumberingSystemName(void) const;
#endif /* U_HIDE_INTERNAL_API */
private:
@ -500,12 +506,13 @@ private:
char actualLocale[ULOC_FULLNAME_CAPACITY];
char validLocale[ULOC_FULLNAME_CAPACITY];
const char16_t* currPattern;
const char16_t* currPattern = nullptr;
UnicodeString currencySpcBeforeSym[UNUM_CURRENCY_SPACING_COUNT];
UnicodeString currencySpcAfterSym[UNUM_CURRENCY_SPACING_COUNT];
UBool fIsCustomCurrencySymbol;
UBool fIsCustomIntlCurrencySymbol;
char nsName[kInternalNumSysNameCapacity+1] = {};
};
// -------------------------------------
@ -591,6 +598,10 @@ inline const char16_t*
DecimalFormatSymbols::getCurrencyPattern() const {
return currPattern;
}
inline const char*
DecimalFormatSymbols::getNumberingSystemName() const {
return nsName;
}
#endif /* U_HIDE_INTERNAL_API */
U_NAMESPACE_END

View file

@ -0,0 +1,215 @@
// © 2022 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#ifndef __FORMATTEDNUMBER_H__
#define __FORMATTEDNUMBER_H__
#include "unicode/utypes.h"
#if U_SHOW_CPLUSPLUS_API
#if !UCONFIG_NO_FORMATTING
#include "unicode/uobject.h"
#include "unicode/formattedvalue.h"
#include "unicode/measunit.h"
#include "unicode/udisplayoptions.h"
/**
* \file
* \brief C API: Formatted number result from various number formatting functions.
*
* See also {@link icu::FormattedValue} for additional things you can do with a FormattedNumber.
*/
U_NAMESPACE_BEGIN
class FieldPositionIteratorHandler;
namespace number { // icu::number
namespace impl {
class DecimalQuantity;
class UFormattedNumberData;
struct UFormattedNumberImpl;
} // icu::number::impl
/**
* The result of a number formatting operation. This class allows the result to be exported in several data types,
* including a UnicodeString and a FieldPositionIterator.
*
* Instances of this class are immutable and thread-safe.
*
* @stable ICU 60
*/
class U_I18N_API FormattedNumber : public UMemory, public FormattedValue {
public:
/**
* Default constructor; makes an empty FormattedNumber.
* @stable ICU 64
*/
FormattedNumber()
: fData(nullptr), fErrorCode(U_INVALID_STATE_ERROR) {}
/**
* Move constructor: Leaves the source FormattedNumber in an undefined state.
* @stable ICU 62
*/
FormattedNumber(FormattedNumber&& src) U_NOEXCEPT;
/**
* Destruct an instance of FormattedNumber.
* @stable ICU 60
*/
virtual ~FormattedNumber() U_OVERRIDE;
/** Copying not supported; use move constructor instead. */
FormattedNumber(const FormattedNumber&) = delete;
/** Copying not supported; use move assignment instead. */
FormattedNumber& operator=(const FormattedNumber&) = delete;
/**
* Move assignment: Leaves the source FormattedNumber in an undefined state.
* @stable ICU 62
*/
FormattedNumber& operator=(FormattedNumber&& src) U_NOEXCEPT;
// Copybrief: this method is older than the parent method
/**
* @copybrief FormattedValue::toString()
*
* For more information, see FormattedValue::toString()
*
* @stable ICU 62
*/
UnicodeString toString(UErrorCode& status) const U_OVERRIDE;
// Copydoc: this method is new in ICU 64
/** @copydoc FormattedValue::toTempString() */
UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE;
// Copybrief: this method is older than the parent method
/**
* @copybrief FormattedValue::appendTo()
*
* For more information, see FormattedValue::appendTo()
*
* @stable ICU 62
*/
Appendable &appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE;
// Copydoc: this method is new in ICU 64
/** @copydoc FormattedValue::nextPosition() */
UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE;
/**
* Export the formatted number as a "numeric string" conforming to the
* syntax defined in the Decimal Arithmetic Specification, available at
* http://speleotrove.com/decimal
*
* This endpoint is useful for obtaining the exact number being printed
* after scaling and rounding have been applied by the number formatter.
*
* Example call site:
*
* auto decimalNumber = fn.toDecimalNumber<std::string>(status);
*
* @tparam StringClass A string class compatible with StringByteSink;
* for example, std::string.
* @param status Set if an error occurs.
* @return A StringClass containing the numeric string.
* @stable ICU 65
*/
template<typename StringClass>
inline StringClass toDecimalNumber(UErrorCode& status) const;
/**
* Gets the resolved output unit.
*
* The output unit is dependent upon the localized preferences for the usage
* specified via NumberFormatterSettings::usage(), and may be a unit with
* UMEASURE_UNIT_MIXED unit complexity (MeasureUnit::getComplexity()), such
* as "foot-and-inch" or "hour-and-minute-and-second".
*
* @return `MeasureUnit`.
* @stable ICU 68
*/
MeasureUnit getOutputUnit(UErrorCode& status) const;
#ifndef U_HIDE_DRAFT_API
/**
* Gets the noun class of the formatted output. Returns `UNDEFINED` when the noun class
* is not supported yet.
*
* @return UDisplayOptionsNounClass
* @draft ICU 72
*/
UDisplayOptionsNounClass getNounClass(UErrorCode &status) const;
#endif // U_HIDE_DRAFT_API
#ifndef U_HIDE_INTERNAL_API
/**
* Gets the raw DecimalQuantity for plural rule selection.
* @internal
*/
void getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const;
/**
* Populates the mutable builder type FieldPositionIteratorHandler.
* @internal
*/
void getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, UErrorCode& status) const;
#endif /* U_HIDE_INTERNAL_API */
private:
// Can't use LocalPointer because UFormattedNumberData is forward-declared
impl::UFormattedNumberData *fData;
// Error code for the terminal methods
UErrorCode fErrorCode;
/**
* Internal constructor from data type. Adopts the data pointer.
* @internal (private)
*/
explicit FormattedNumber(impl::UFormattedNumberData *results)
: fData(results), fErrorCode(U_ZERO_ERROR) {}
explicit FormattedNumber(UErrorCode errorCode)
: fData(nullptr), fErrorCode(errorCode) {}
void toDecimalNumber(ByteSink& sink, UErrorCode& status) const;
// To give LocalizedNumberFormatter format methods access to this class's constructor:
friend class LocalizedNumberFormatter;
friend class SimpleNumberFormatter;
// To give C API access to internals
friend struct impl::UFormattedNumberImpl;
};
template<typename StringClass>
StringClass FormattedNumber::toDecimalNumber(UErrorCode& status) const {
StringClass result;
StringByteSink<StringClass> sink(&result);
toDecimalNumber(sink, status);
return result;
}
} // namespace number
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif /* U_SHOW_CPLUSPLUS_API */
#endif // __FORMATTEDNUMBER_H__

View file

@ -16,17 +16,17 @@
#include "unicode/dcfmtsym.h"
#include "unicode/displayoptions.h"
#include "unicode/fieldpos.h"
#include "unicode/formattedvalue.h"
#include "unicode/fpositer.h"
#include "unicode/measunit.h"
#include "unicode/nounit.h"
#include "unicode/parseerr.h"
#include "unicode/plurrule.h"
#include "unicode/ucurr.h"
#include "unicode/udisplayoptions.h"
#include "unicode/unum.h"
#include "unicode/unumberformatter.h"
#include "unicode/uobject.h"
#include "unicode/unumberoptions.h"
#include "unicode/formattednumber.h"
/**
* \file
@ -112,6 +112,7 @@ namespace number { // icu::number
// Forward declarations:
class UnlocalizedNumberFormatter;
class LocalizedNumberFormatter;
class SimpleNumberFormatter;
class FormattedNumber;
class Notation;
class ScientificNotation;
@ -166,6 +167,8 @@ struct UFormattedNumberImpl;
class MutablePatternModifier;
class ImmutablePatternModifier;
struct DecimalFormatWarehouse;
struct SimpleMicroProps;
class AdoptingSignumModifierStore;
/**
* Used for NumberRangeFormatter and implemented in numrange_fluent.cpp.
@ -1436,9 +1439,11 @@ class U_I18N_API Grouper : public UMemory {
// To allow MacroProps/MicroProps to initialize empty instances:
friend struct MacroProps;
friend struct MicroProps;
friend struct SimpleMicroProps;
// To allow NumberFormatterImpl to access isBogus() and perform other operations:
friend class NumberFormatterImpl;
friend class ::icu::number::SimpleNumberFormatter;
// To allow NumberParserImpl to perform setLocaleData():
friend class ::icu::numparse::impl::NumberParserImpl;
@ -2692,173 +2697,6 @@ class U_I18N_API LocalizedNumberFormatter
#pragma warning(pop)
#endif
/**
* The result of a number formatting operation. This class allows the result to be exported in several data types,
* including a UnicodeString and a FieldPositionIterator.
*
* Instances of this class are immutable and thread-safe.
*
* @stable ICU 60
*/
class U_I18N_API FormattedNumber : public UMemory, public FormattedValue {
public:
/**
* Default constructor; makes an empty FormattedNumber.
* @stable ICU 64
*/
FormattedNumber()
: fData(nullptr), fErrorCode(U_INVALID_STATE_ERROR) {}
/**
* Move constructor: Leaves the source FormattedNumber in an undefined state.
* @stable ICU 62
*/
FormattedNumber(FormattedNumber&& src) U_NOEXCEPT;
/**
* Destruct an instance of FormattedNumber.
* @stable ICU 60
*/
virtual ~FormattedNumber() U_OVERRIDE;
/** Copying not supported; use move constructor instead. */
FormattedNumber(const FormattedNumber&) = delete;
/** Copying not supported; use move assignment instead. */
FormattedNumber& operator=(const FormattedNumber&) = delete;
/**
* Move assignment: Leaves the source FormattedNumber in an undefined state.
* @stable ICU 62
*/
FormattedNumber& operator=(FormattedNumber&& src) U_NOEXCEPT;
// Copybrief: this method is older than the parent method
/**
* @copybrief FormattedValue::toString()
*
* For more information, see FormattedValue::toString()
*
* @stable ICU 62
*/
UnicodeString toString(UErrorCode& status) const U_OVERRIDE;
// Copydoc: this method is new in ICU 64
/** @copydoc FormattedValue::toTempString() */
UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE;
// Copybrief: this method is older than the parent method
/**
* @copybrief FormattedValue::appendTo()
*
* For more information, see FormattedValue::appendTo()
*
* @stable ICU 62
*/
Appendable &appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE;
// Copydoc: this method is new in ICU 64
/** @copydoc FormattedValue::nextPosition() */
UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE;
/**
* Export the formatted number as a "numeric string" conforming to the
* syntax defined in the Decimal Arithmetic Specification, available at
* http://speleotrove.com/decimal
*
* This endpoint is useful for obtaining the exact number being printed
* after scaling and rounding have been applied by the number formatter.
*
* Example call site:
*
* auto decimalNumber = fn.toDecimalNumber<std::string>(status);
*
* @tparam StringClass A string class compatible with StringByteSink;
* for example, std::string.
* @param status Set if an error occurs.
* @return A StringClass containing the numeric string.
* @stable ICU 65
*/
template<typename StringClass>
inline StringClass toDecimalNumber(UErrorCode& status) const;
/**
* Gets the resolved output unit.
*
* The output unit is dependent upon the localized preferences for the usage
* specified via NumberFormatterSettings::usage(), and may be a unit with
* UMEASURE_UNIT_MIXED unit complexity (MeasureUnit::getComplexity()), such
* as "foot-and-inch" or "hour-and-minute-and-second".
*
* @return `MeasureUnit`.
* @stable ICU 68
*/
MeasureUnit getOutputUnit(UErrorCode& status) const;
#ifndef U_HIDE_DRAFT_API
/**
* Gets the noun class of the formatted output. Returns `UNDEFINED` when the noun class
* is not supported yet.
*
* @return UDisplayOptionsNounClass
* @draft ICU 72
*/
UDisplayOptionsNounClass getNounClass(UErrorCode &status) const;
#endif // U_HIDE_DRAFT_API
#ifndef U_HIDE_INTERNAL_API
/**
* Gets the raw DecimalQuantity for plural rule selection.
* @internal
*/
void getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const;
/**
* Populates the mutable builder type FieldPositionIteratorHandler.
* @internal
*/
void getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, UErrorCode& status) const;
#endif /* U_HIDE_INTERNAL_API */
private:
// Can't use LocalPointer because UFormattedNumberData is forward-declared
const impl::UFormattedNumberData *fData;
// Error code for the terminal methods
UErrorCode fErrorCode;
/**
* Internal constructor from data type. Adopts the data pointer.
* @internal (private)
*/
explicit FormattedNumber(impl::UFormattedNumberData *results)
: fData(results), fErrorCode(U_ZERO_ERROR) {}
explicit FormattedNumber(UErrorCode errorCode)
: fData(nullptr), fErrorCode(errorCode) {}
void toDecimalNumber(ByteSink& sink, UErrorCode& status) const;
// To give LocalizedNumberFormatter format methods access to this class's constructor:
friend class LocalizedNumberFormatter;
// To give C API access to internals
friend struct impl::UFormattedNumberImpl;
};
template<typename StringClass>
StringClass FormattedNumber::toDecimalNumber(UErrorCode& status) const {
StringClass result;
StringByteSink<StringClass> sink(&result);
toDecimalNumber(sink, status);
return result;
}
/**
* See the main description in numberformatter.h for documentation and examples.
*

View file

@ -0,0 +1,329 @@
// © 2022 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#ifndef __SIMPLENUMBERFORMATTERH__
#define __SIMPLENUMBERFORMATTERH__
#include "unicode/utypes.h"
#if U_SHOW_CPLUSPLUS_API
#if !UCONFIG_NO_FORMATTING
#include "unicode/dcfmtsym.h"
#include "unicode/usimplenumberformatter.h"
#include "unicode/formattednumber.h"
/**
* \file
* \brief C++ API: Simple number formatting focused on low memory and code size.
*
* These functions render locale-aware number strings but without the bells and whistles found in
* other number formatting APIs such as those in numberformatter.h, like units and currencies.
*
* <pre>
* SimpleNumberFormatter snf = SimpleNumberFormatter::forLocale("de-CH", status);
* FormattedNumber result = snf.formatInt64(-1000007, status);
* assertEquals("", u"-1000007", result.toString(status));
* </pre>
*/
U_NAMESPACE_BEGIN
namespace number { // icu::number
namespace impl {
class UFormattedNumberData;
struct SimpleMicroProps;
class AdoptingSignumModifierStore;
} // icu::number::impl
#ifndef U_HIDE_DRAFT_API
/**
* An input type for SimpleNumberFormatter.
*
* This class is mutable and not intended for public subclassing. This class is movable but not copyable.
*
* @draft ICU 73
*/
class U_I18N_API SimpleNumber : public UMemory {
public:
/**
* Creates a SimpleNumber for an integer.
*
* @draft ICU 73
*/
static SimpleNumber forInt64(int64_t value, UErrorCode& status);
/**
* Changes the value of the SimpleNumber by a power of 10.
*
* This function immediately mutates the inner value.
*
* @draft ICU 73
*/
void multiplyByPowerOfTen(int32_t power, UErrorCode& status);
/**
* Rounds the value currently stored in the SimpleNumber to the given power of 10.
*
* This function immediately mutates the inner value.
*
* @draft ICU 73
*/
void roundTo(int32_t power, UNumberFormatRoundingMode roundingMode, UErrorCode& status);
/**
* Truncates the most significant digits to the given maximum number of integer digits.
*
* This function immediately mutates the inner value.
*
* @draft ICU 73
*/
void truncateStart(uint32_t maximumIntegerDigits, UErrorCode& status);
/**
* Pads the beginning of the number with zeros up to the given minimum number of integer digits.
*
* This setting is applied upon formatting the number.
*
* @draft ICU 73
*/
void setMinimumIntegerDigits(uint32_t minimumIntegerDigits, UErrorCode& status);
/**
* Pads the end of the number with zeros up to the given minimum number of fraction digits.
*
* This setting is applied upon formatting the number.
*
* @draft ICU 73
*/
void setMinimumFractionDigits(uint32_t minimumFractionDigits, UErrorCode& status);
/**
* Sets the sign of the number: an explicit plus sign, explicit minus sign, or no sign.
*
* This setting is applied upon formatting the number.
*
* NOTE: This does not support accounting sign notation.
*
* @draft ICU 73
*/
void setSign(USimpleNumberSign sign, UErrorCode& status);
/**
* Creates a new, empty SimpleNumber that does not contain a value.
*
* NOTE: This number will fail to format; use forInt64() to create a SimpleNumber with a value.
*
* @draft ICU 73
*/
SimpleNumber() = default;
/**
* Destruct this SimpleNumber, cleaning up any memory it might own.
*
* @draft ICU 73
*/
~SimpleNumber() {
cleanup();
}
/**
* SimpleNumber move constructor.
*
* @draft ICU 73
*/
SimpleNumber(SimpleNumber&& other) U_NOEXCEPT {
fData = other.fData;
fSign = other.fSign;
other.fData = nullptr;
}
/**
* SimpleNumber move assignment.
*
* @draft ICU 73
*/
SimpleNumber& operator=(SimpleNumber&& other) U_NOEXCEPT {
cleanup();
fData = other.fData;
fSign = other.fSign;
other.fData = nullptr;
return *this;
}
private:
SimpleNumber(impl::UFormattedNumberData* data, UErrorCode& status);
SimpleNumber(const SimpleNumber&) = delete;
SimpleNumber& operator=(const SimpleNumber&) = delete;
void cleanup();
impl::UFormattedNumberData* fData = nullptr;
USimpleNumberSign fSign = UNUM_SIMPLE_NUMBER_NO_SIGN;
friend class SimpleNumberFormatter;
};
/**
* A special NumberFormatter focused on smaller binary size and memory use.
*
* SimpleNumberFormatter is capable of basic number formatting, including grouping separators,
* sign display, and rounding. It is not capable of currencies, compact notation, or units.
*
* This class is immutable and not intended for public subclassing. This class is movable but not copyable.
*
* @draft ICU 73
*/
class U_I18N_API SimpleNumberFormatter : public UMemory {
public:
/**
* Creates a new SimpleNumberFormatter with all locale defaults.
*
* @draft ICU 73
*/
static SimpleNumberFormatter forLocale(
const icu::Locale &locale,
UErrorCode &status);
/**
* Creates a new SimpleNumberFormatter, overriding the grouping strategy.
*
* @draft ICU 73
*/
static SimpleNumberFormatter forLocaleAndGroupingStrategy(
const icu::Locale &locale,
UNumberGroupingStrategy groupingStrategy,
UErrorCode &status);
/**
* Creates a new SimpleNumberFormatter, overriding the grouping strategy and symbols.
*
* IMPORTANT: For efficiency, this function borrows the symbols. The symbols MUST remain valid
* for the lifetime of the SimpleNumberFormatter.
*
* @draft ICU 73
*/
static SimpleNumberFormatter forLocaleAndSymbolsAndGroupingStrategy(
const icu::Locale &locale,
const DecimalFormatSymbols &symbols,
UNumberGroupingStrategy groupingStrategy,
UErrorCode &status);
/**
* Formats a value using this SimpleNumberFormatter.
*
* The SimpleNumber argument is "consumed". A new SimpleNumber object should be created for
* every formatting operation.
*
* @draft ICU 73
*/
FormattedNumber format(SimpleNumber value, UErrorCode &status) const;
/**
* Formats an integer using this SimpleNumberFormatter.
*
* For more control over the formatting, use SimpleNumber.
*
* @draft ICU 73
*/
FormattedNumber formatInt64(int64_t value, UErrorCode &status) const {
return format(SimpleNumber::forInt64(value, status), status);
}
#ifndef U_HIDE_INTERNAL_API
/**
* Run the formatter with the internal types.
* @internal
*/
void formatImpl(impl::UFormattedNumberData* data, USimpleNumberSign sign, UErrorCode& status) const;
#endif // U_HIDE_INTERNAL_API
/**
* Destruct this SimpleNumberFormatter, cleaning up any memory it might own.
*
* @draft ICU 73
*/
~SimpleNumberFormatter() {
cleanup();
}
/**
* Creates a shell, initialized but non-functional SimpleNumberFormatter.
*
* @draft ICU 73
*/
SimpleNumberFormatter() = default;
/**
* SimpleNumberFormatter: Move constructor.
*
* @draft ICU 73
*/
SimpleNumberFormatter(SimpleNumberFormatter&& other) U_NOEXCEPT {
fGroupingStrategy = other.fGroupingStrategy;
fOwnedSymbols = other.fOwnedSymbols;
fMicros = other.fMicros;
fPatternModifier = other.fPatternModifier;
other.fOwnedSymbols = nullptr;
other.fMicros = nullptr;
other.fPatternModifier = nullptr;
}
/**
* SimpleNumberFormatter: Move assignment.
*
* @draft ICU 73
*/
SimpleNumberFormatter& operator=(SimpleNumberFormatter&& other) U_NOEXCEPT {
cleanup();
fGroupingStrategy = other.fGroupingStrategy;
fOwnedSymbols = other.fOwnedSymbols;
fMicros = other.fMicros;
fPatternModifier = other.fPatternModifier;
other.fOwnedSymbols = nullptr;
other.fMicros = nullptr;
other.fPatternModifier = nullptr;
return *this;
}
private:
void initialize(
const icu::Locale &locale,
const DecimalFormatSymbols &symbols,
UNumberGroupingStrategy groupingStrategy,
UErrorCode &status);
void cleanup();
SimpleNumberFormatter(const SimpleNumberFormatter&) = delete;
SimpleNumberFormatter& operator=(const SimpleNumberFormatter&) = delete;
UNumberGroupingStrategy fGroupingStrategy = UNUM_GROUPING_AUTO;
// Owned Pointers:
DecimalFormatSymbols* fOwnedSymbols = nullptr; // can be empty
impl::SimpleMicroProps* fMicros = nullptr;
impl::AdoptingSignumModifierStore* fPatternModifier = nullptr;
};
#endif // U_HIDE_DRAFT_API
} // namespace number
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif /* U_SHOW_CPLUSPLUS_API */
#endif // __SIMPLENUMBERFORMATTERH__

View file

@ -0,0 +1,224 @@
// © 2022 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#ifndef __UFORMATTEDNUMBER_H__
#define __UFORMATTEDNUMBER_H__
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/ufieldpositer.h"
#include "unicode/uformattedvalue.h"
#include "unicode/umisc.h"
/**
* \file
* \brief C API: Formatted number result from various number formatting functions.
*
* Create a `UFormattedNumber` to hold the result of a number formatting operation. The same
* `UFormattedNumber` can be reused multiple times.
*
* <pre>
* LocalUFormattedNumberPointer uresult(unumf_openResult(status));
*
* // pass uresult.getAlias() to your number formatter
*
* int32_t length;
* const UChar* s = ufmtval_getString(unumf_resultAsValue(uresult.getAlias(), status), &length, status));
*
* // The string result is in `s` with the given `length` (it is also NUL-terminated).
* </pre>
*/
struct UFormattedNumber;
/**
* C-compatible version of icu::number::FormattedNumber.
*
* NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
*
* @stable ICU 62
*/
typedef struct UFormattedNumber UFormattedNumber;
/**
* Creates an object to hold the result of a UNumberFormatter
* operation. The object can be used repeatedly; it is cleared whenever
* passed to a format function.
*
* @param ec Set if an error occurs.
* @stable ICU 62
*/
U_CAPI UFormattedNumber* U_EXPORT2
unumf_openResult(UErrorCode* ec);
/**
* Returns a representation of a UFormattedNumber as a UFormattedValue,
* which can be subsequently passed to any API requiring that type.
*
* The returned object is owned by the UFormattedNumber and is valid
* only as long as the UFormattedNumber is present and unchanged in memory.
*
* You can think of this method as a cast between types.
*
* @param uresult The object containing the formatted string.
* @param ec Set if an error occurs.
* @return A UFormattedValue owned by the input object.
* @stable ICU 64
*/
U_CAPI const UFormattedValue* U_EXPORT2
unumf_resultAsValue(const UFormattedNumber* uresult, UErrorCode* ec);
/**
* Extracts the result number string out of a UFormattedNumber to a UChar buffer if possible.
* If bufferCapacity is greater than the required length, a terminating NUL is written.
* If bufferCapacity is less than the required length, an error code is set.
*
* Also see ufmtval_getString, which returns a NUL-terminated string:
*
* int32_t len;
* const UChar* str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), &len, &ec);
*
* NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
*
* @param uresult The object containing the formatted number.
* @param buffer Where to save the string output.
* @param bufferCapacity The number of UChars available in the buffer.
* @param ec Set if an error occurs.
* @return The required length.
* @stable ICU 62
*/
U_CAPI int32_t U_EXPORT2
unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t bufferCapacity,
UErrorCode* ec);
/**
* Determines the start and end indices of the next occurrence of the given <em>field</em> in the
* output string. This allows you to determine the locations of, for example, the integer part,
* fraction part, or symbols.
*
* This is a simpler but less powerful alternative to {@link ufmtval_nextPosition}.
*
* If a field occurs just once, calling this method will find that occurrence and return it. If a
* field occurs multiple times, this method may be called repeatedly with the following pattern:
*
* <pre>
* UFieldPosition ufpos = {UNUM_GROUPING_SEPARATOR_FIELD, 0, 0};
* while (unumf_resultNextFieldPosition(uresult, ufpos, &ec)) {
* // do something with ufpos.
* }
* </pre>
*
* This method is useful if you know which field to query. If you want all available field position
* information, use unumf_resultGetAllFieldPositions().
*
* NOTE: All fields of the UFieldPosition must be initialized before calling this method.
*
* @param uresult The object containing the formatted number.
* @param ufpos
* Input+output variable. On input, the "field" property determines which field to look up,
* and the "endIndex" property determines where to begin the search. On output, the
* "beginIndex" field is set to the beginning of the first occurrence of the field after the
* input "endIndex", and "endIndex" is set to the end of that occurrence of the field
* (exclusive index). If a field position is not found, the FieldPosition is not changed and
* the method returns false.
* @param ec Set if an error occurs.
* @stable ICU 62
*/
U_CAPI UBool U_EXPORT2
unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec);
/**
* Populates the given iterator with all fields in the formatted output string. This allows you to
* determine the locations of the integer part, fraction part, and sign.
*
* This is an alternative to the more powerful {@link ufmtval_nextPosition} API.
*
* If you need information on only one field, use {@link ufmtval_nextPosition} or
* {@link unumf_resultNextFieldPosition}.
*
* @param uresult The object containing the formatted number.
* @param ufpositer
* A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open}. Iteration
* information already present in the UFieldPositionIterator is deleted, and the iterator is reset
* to apply to the fields in the formatted string created by this function call. The field values
* and indexes returned by {@link #ufieldpositer_next} represent fields denoted by
* the UNumberFormatFields enum. Fields are not returned in a guaranteed order. Fields cannot
* overlap, but they may nest. For example, 1234 could format as "1,234" which might consist of a
* grouping separator field for ',' and an integer field encompassing the entire string.
* @param ec Set if an error occurs.
* @stable ICU 62
*/
U_CAPI void U_EXPORT2
unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
UErrorCode* ec);
/**
* Extracts the formatted number as a "numeric string" conforming to the
* syntax defined in the Decimal Arithmetic Specification, available at
* http://speleotrove.com/decimal
*
* This endpoint is useful for obtaining the exact number being printed
* after scaling and rounding have been applied by the number formatter.
*
* @param uresult The input object containing the formatted number.
* @param dest the 8-bit char buffer into which the decimal number is placed
* @param destCapacity The size, in chars, of the destination buffer. May be zero
* for precomputing the required size.
* @param ec receives any error status.
* If U_BUFFER_OVERFLOW_ERROR: Returns number of chars for
* preflighting.
* @return Number of chars in the data. Does not include a trailing NUL.
* @stable ICU 68
*/
U_CAPI int32_t U_EXPORT2
unumf_resultToDecimalNumber(
const UFormattedNumber* uresult,
char* dest,
int32_t destCapacity,
UErrorCode* ec);
/**
* Releases the UFormattedNumber created by unumf_openResult().
*
* @param uresult An object created by unumf_openResult().
* @stable ICU 62
*/
U_CAPI void U_EXPORT2
unumf_closeResult(UFormattedNumber* uresult);
#if U_SHOW_CPLUSPLUS_API
U_NAMESPACE_BEGIN
/**
* \class LocalUFormattedNumberPointer
* "Smart pointer" class; closes a UFormattedNumber via unumf_closeResult().
* For most methods see the LocalPointerBase base class.
*
* Usage:
* <pre>
* LocalUFormattedNumberPointer uformatter(unumf_openResult(...));
* // no need to explicitly call unumf_closeResult()
* </pre>
*
* @see LocalPointerBase
* @see LocalPointer
* @stable ICU 62
*/
U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedNumberPointer, UFormattedNumber, unumf_closeResult);
U_NAMESPACE_END
#endif // U_SHOW_CPLUSPLUS_API
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif //__UFORMATTEDNUMBER_H__

View file

@ -25,6 +25,7 @@
#include "unicode/uformattable.h"
#include "unicode/udisplaycontext.h"
#include "unicode/ufieldpositer.h"
#include "unicode/unumberoptions.h"
#if U_SHOW_CPLUSPLUS_API
#include "unicode/localpointer.h"
@ -271,55 +272,6 @@ typedef enum UNumberFormatStyle {
UNUM_IGNORE = UNUM_PATTERN_DECIMAL
} UNumberFormatStyle;
/** The possible number format rounding modes.
*
* <p>
* For more detail on rounding modes, see:
* https://unicode-org.github.io/icu/userguide/format_parse/numbers/rounding-modes
*
* @stable ICU 2.0
*/
typedef enum UNumberFormatRoundingMode {
UNUM_ROUND_CEILING,
UNUM_ROUND_FLOOR,
UNUM_ROUND_DOWN,
UNUM_ROUND_UP,
/**
* Half-even rounding
* @stable, ICU 3.8
*/
UNUM_ROUND_HALFEVEN,
#ifndef U_HIDE_DEPRECATED_API
/**
* Half-even rounding, misspelled name
* @deprecated, ICU 3.8
*/
UNUM_FOUND_HALFEVEN = UNUM_ROUND_HALFEVEN,
#endif /* U_HIDE_DEPRECATED_API */
UNUM_ROUND_HALFDOWN = UNUM_ROUND_HALFEVEN + 1,
UNUM_ROUND_HALFUP,
/**
* ROUND_UNNECESSARY reports an error if formatted result is not exact.
* @stable ICU 4.8
*/
UNUM_ROUND_UNNECESSARY,
/**
* Rounds ties toward the odd number.
* @stable ICU 69
*/
UNUM_ROUND_HALF_ODD,
/**
* Rounds ties toward +.
* @stable ICU 69
*/
UNUM_ROUND_HALF_CEILING,
/**
* Rounds ties toward -.
* @stable ICU 69
*/
UNUM_ROUND_HALF_FLOOR,
} UNumberFormatRoundingMode;
/** The possible number format pad positions.
* @stable ICU 2.0
*/

View file

@ -9,9 +9,8 @@
#if !UCONFIG_NO_FORMATTING
#include "unicode/parseerr.h"
#include "unicode/ufieldpositer.h"
#include "unicode/umisc.h"
#include "unicode/uformattedvalue.h"
#include "unicode/unumberoptions.h"
#include "unicode/uformattednumber.h"
/**
@ -243,107 +242,6 @@ typedef enum UNumberUnitWidth {
UNUM_UNIT_WIDTH_COUNT = 7
} UNumberUnitWidth;
/**
* An enum declaring the strategy for when and how to display grouping separators (i.e., the
* separator, often a comma or period, after every 2-3 powers of ten). The choices are several
* pre-built strategies for different use cases that employ locale data whenever possible. Example
* outputs for 1234 and 1234567 in <em>en-IN</em>:
*
* <ul>
* <li>OFF: 1234 and 12345
* <li>MIN2: 1234 and 12,34,567
* <li>AUTO: 1,234 and 12,34,567
* <li>ON_ALIGNED: 1,234 and 12,34,567
* <li>THOUSANDS: 1,234 and 1,234,567
* </ul>
*
* <p>
* The default is AUTO, which displays grouping separators unless the locale data says that grouping
* is not customary. To force grouping for all numbers greater than 1000 consistently across locales,
* use ON_ALIGNED. On the other hand, to display grouping less frequently than the default, use MIN2
* or OFF. See the docs of each option for details.
*
* <p>
* Note: This enum specifies the strategy for grouping sizes. To set which character to use as the
* grouping separator, use the "symbols" setter.
*
* @stable ICU 63
*/
typedef enum UNumberGroupingStrategy {
/**
* Do not display grouping separators in any locale.
*
* @stable ICU 61
*/
UNUM_GROUPING_OFF,
/**
* Display grouping using locale defaults, except do not show grouping on values smaller than
* 10000 (such that there is a <em>minimum of two digits</em> before the first separator).
*
* <p>
* Note that locales may restrict grouping separators to be displayed only on 1 million or
* greater (for example, ee and hu) or disable grouping altogether (for example, bg currency).
*
* <p>
* Locale data is used to determine whether to separate larger numbers into groups of 2
* (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
*
* @stable ICU 61
*/
UNUM_GROUPING_MIN2,
/**
* Display grouping using the default strategy for all locales. This is the default behavior.
*
* <p>
* Note that locales may restrict grouping separators to be displayed only on 1 million or
* greater (for example, ee and hu) or disable grouping altogether (for example, bg currency).
*
* <p>
* Locale data is used to determine whether to separate larger numbers into groups of 2
* (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
*
* @stable ICU 61
*/
UNUM_GROUPING_AUTO,
/**
* Always display the grouping separator on values of at least 1000.
*
* <p>
* This option ignores the locale data that restricts or disables grouping, described in MIN2 and
* AUTO. This option may be useful to normalize the alignment of numbers, such as in a
* spreadsheet.
*
* <p>
* Locale data is used to determine whether to separate larger numbers into groups of 2
* (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
*
* @stable ICU 61
*/
UNUM_GROUPING_ON_ALIGNED,
/**
* Use the Western defaults: groups of 3 and enabled for all numbers 1000 or greater. Do not use
* locale data for determining the grouping strategy.
*
* @stable ICU 61
*/
UNUM_GROUPING_THOUSANDS
#ifndef U_HIDE_INTERNAL_API
,
/**
* One more than the highest UNumberGroupingStrategy value.
*
* @internal ICU 62: The numeric value may change over time; see ICU ticket #12420.
*/
UNUM_GROUPING_COUNT
#endif /* U_HIDE_INTERNAL_API */
} UNumberGroupingStrategy;
/**
* An enum declaring how to denote positive and negative numbers. Example outputs when formatting
* 123, 0, and -123 in <em>en-US</em>:
@ -528,16 +426,6 @@ struct UNumberFormatter;
*/
typedef struct UNumberFormatter UNumberFormatter;
struct UFormattedNumber;
/**
* C-compatible version of icu::number::FormattedNumber.
*
* NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
*
* @stable ICU 62
*/
typedef struct UFormattedNumber UFormattedNumber;
/**
* Creates a new UNumberFormatter for the given skeleton string and locale. This is currently the only
@ -584,17 +472,6 @@ unumf_openForSkeletonAndLocaleWithError(
const UChar* skeleton, int32_t skeletonLen, const char* locale, UParseError* perror, UErrorCode* ec);
/**
* Creates an object to hold the result of a UNumberFormatter
* operation. The object can be used repeatedly; it is cleared whenever
* passed to a format function.
*
* @param ec Set if an error occurs.
* @stable ICU 62
*/
U_CAPI UFormattedNumber* U_EXPORT2
unumf_openResult(UErrorCode* ec);
/**
* Uses a UNumberFormatter to format an integer to a UFormattedNumber. A string, field position, and other
@ -659,135 +536,6 @@ U_CAPI void U_EXPORT2
unumf_formatDecimal(const UNumberFormatter* uformatter, const char* value, int32_t valueLen,
UFormattedNumber* uresult, UErrorCode* ec);
/**
* Returns a representation of a UFormattedNumber as a UFormattedValue,
* which can be subsequently passed to any API requiring that type.
*
* The returned object is owned by the UFormattedNumber and is valid
* only as long as the UFormattedNumber is present and unchanged in memory.
*
* You can think of this method as a cast between types.
*
* @param uresult The object containing the formatted string.
* @param ec Set if an error occurs.
* @return A UFormattedValue owned by the input object.
* @stable ICU 64
*/
U_CAPI const UFormattedValue* U_EXPORT2
unumf_resultAsValue(const UFormattedNumber* uresult, UErrorCode* ec);
/**
* Extracts the result number string out of a UFormattedNumber to a UChar buffer if possible.
* If bufferCapacity is greater than the required length, a terminating NUL is written.
* If bufferCapacity is less than the required length, an error code is set.
*
* Also see ufmtval_getString, which returns a NUL-terminated string:
*
* int32_t len;
* const UChar* str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), &len, &ec);
*
* NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
*
* @param uresult The object containing the formatted number.
* @param buffer Where to save the string output.
* @param bufferCapacity The number of UChars available in the buffer.
* @param ec Set if an error occurs.
* @return The required length.
* @stable ICU 62
*/
U_CAPI int32_t U_EXPORT2
unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t bufferCapacity,
UErrorCode* ec);
/**
* Determines the start and end indices of the next occurrence of the given <em>field</em> in the
* output string. This allows you to determine the locations of, for example, the integer part,
* fraction part, or symbols.
*
* This is a simpler but less powerful alternative to {@link ufmtval_nextPosition}.
*
* If a field occurs just once, calling this method will find that occurrence and return it. If a
* field occurs multiple times, this method may be called repeatedly with the following pattern:
*
* <pre>
* UFieldPosition ufpos = {UNUM_GROUPING_SEPARATOR_FIELD, 0, 0};
* while (unumf_resultNextFieldPosition(uresult, ufpos, &ec)) {
* // do something with ufpos.
* }
* </pre>
*
* This method is useful if you know which field to query. If you want all available field position
* information, use unumf_resultGetAllFieldPositions().
*
* NOTE: All fields of the UFieldPosition must be initialized before calling this method.
*
* @param uresult The object containing the formatted number.
* @param ufpos
* Input+output variable. On input, the "field" property determines which field to look up,
* and the "endIndex" property determines where to begin the search. On output, the
* "beginIndex" field is set to the beginning of the first occurrence of the field after the
* input "endIndex", and "endIndex" is set to the end of that occurrence of the field
* (exclusive index). If a field position is not found, the FieldPosition is not changed and
* the method returns false.
* @param ec Set if an error occurs.
* @stable ICU 62
*/
U_CAPI UBool U_EXPORT2
unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec);
/**
* Populates the given iterator with all fields in the formatted output string. This allows you to
* determine the locations of the integer part, fraction part, and sign.
*
* This is an alternative to the more powerful {@link ufmtval_nextPosition} API.
*
* If you need information on only one field, use {@link ufmtval_nextPosition} or
* {@link unumf_resultNextFieldPosition}.
*
* @param uresult The object containing the formatted number.
* @param ufpositer
* A pointer to a UFieldPositionIterator created by {@link #ufieldpositer_open}. Iteration
* information already present in the UFieldPositionIterator is deleted, and the iterator is reset
* to apply to the fields in the formatted string created by this function call. The field values
* and indexes returned by {@link #ufieldpositer_next} represent fields denoted by
* the UNumberFormatFields enum. Fields are not returned in a guaranteed order. Fields cannot
* overlap, but they may nest. For example, 1234 could format as "1,234" which might consist of a
* grouping separator field for ',' and an integer field encompassing the entire string.
* @param ec Set if an error occurs.
* @stable ICU 62
*/
U_CAPI void U_EXPORT2
unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
UErrorCode* ec);
/**
* Extracts the formatted number as a "numeric string" conforming to the
* syntax defined in the Decimal Arithmetic Specification, available at
* http://speleotrove.com/decimal
*
* This endpoint is useful for obtaining the exact number being printed
* after scaling and rounding have been applied by the number formatter.
*
* @param uresult The input object containing the formatted number.
* @param dest the 8-bit char buffer into which the decimal number is placed
* @param destCapacity The size, in chars, of the destination buffer. May be zero
* for precomputing the required size.
* @param ec receives any error status.
* If U_BUFFER_OVERFLOW_ERROR: Returns number of chars for
* preflighting.
* @return Number of chars in the data. Does not include a trailing NUL.
* @stable ICU 68
*/
U_CAPI int32_t U_EXPORT2
unumf_resultToDecimalNumber(
const UFormattedNumber* uresult,
char* dest,
int32_t destCapacity,
UErrorCode* ec);
/**
@ -800,15 +548,6 @@ U_CAPI void U_EXPORT2
unumf_close(UNumberFormatter* uformatter);
/**
* Releases the UFormattedNumber created by unumf_openResult().
*
* @param uresult An object created by unumf_openResult().
* @stable ICU 62
*/
U_CAPI void U_EXPORT2
unumf_closeResult(UFormattedNumber* uresult);
#if U_SHOW_CPLUSPLUS_API
U_NAMESPACE_BEGIN
@ -830,23 +569,6 @@ U_NAMESPACE_BEGIN
*/
U_DEFINE_LOCAL_OPEN_POINTER(LocalUNumberFormatterPointer, UNumberFormatter, unumf_close);
/**
* \class LocalUFormattedNumberPointer
* "Smart pointer" class; closes a UFormattedNumber via unumf_closeResult().
* For most methods see the LocalPointerBase base class.
*
* Usage:
* <pre>
* LocalUFormattedNumberPointer uformatter(unumf_openResult(...));
* // no need to explicitly call unumf_closeResult()
* </pre>
*
* @see LocalPointerBase
* @see LocalPointer
* @stable ICU 62
*/
U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedNumberPointer, UFormattedNumber, unumf_closeResult);
U_NAMESPACE_END
#endif // U_SHOW_CPLUSPLUS_API

View file

@ -0,0 +1,173 @@
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#ifndef __UNUMBEROPTIONS_H__
#define __UNUMBEROPTIONS_H__
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
/**
* \file
* \brief C API: Header-only input options for various number formatting APIs.
*
* You do not normally need to include this header file directly, because it is included in all
* files that use these enums.
*/
/** The possible number format rounding modes.
*
* <p>
* For more detail on rounding modes, see:
* https://unicode-org.github.io/icu/userguide/format_parse/numbers/rounding-modes
*
* @stable ICU 2.0
*/
typedef enum UNumberFormatRoundingMode {
UNUM_ROUND_CEILING,
UNUM_ROUND_FLOOR,
UNUM_ROUND_DOWN,
UNUM_ROUND_UP,
/**
* Half-even rounding
* @stable, ICU 3.8
*/
UNUM_ROUND_HALFEVEN,
#ifndef U_HIDE_DEPRECATED_API
/**
* Half-even rounding, misspelled name
* @deprecated, ICU 3.8
*/
UNUM_FOUND_HALFEVEN = UNUM_ROUND_HALFEVEN,
#endif /* U_HIDE_DEPRECATED_API */
UNUM_ROUND_HALFDOWN = UNUM_ROUND_HALFEVEN + 1,
UNUM_ROUND_HALFUP,
/**
* ROUND_UNNECESSARY reports an error if formatted result is not exact.
* @stable ICU 4.8
*/
UNUM_ROUND_UNNECESSARY,
/**
* Rounds ties toward the odd number.
* @stable ICU 69
*/
UNUM_ROUND_HALF_ODD,
/**
* Rounds ties toward +.
* @stable ICU 69
*/
UNUM_ROUND_HALF_CEILING,
/**
* Rounds ties toward -.
* @stable ICU 69
*/
UNUM_ROUND_HALF_FLOOR,
} UNumberFormatRoundingMode;
/**
* An enum declaring the strategy for when and how to display grouping separators (i.e., the
* separator, often a comma or period, after every 2-3 powers of ten). The choices are several
* pre-built strategies for different use cases that employ locale data whenever possible. Example
* outputs for 1234 and 1234567 in <em>en-IN</em>:
*
* <ul>
* <li>OFF: 1234 and 12345
* <li>MIN2: 1234 and 12,34,567
* <li>AUTO: 1,234 and 12,34,567
* <li>ON_ALIGNED: 1,234 and 12,34,567
* <li>THOUSANDS: 1,234 and 1,234,567
* </ul>
*
* <p>
* The default is AUTO, which displays grouping separators unless the locale data says that grouping
* is not customary. To force grouping for all numbers greater than 1000 consistently across locales,
* use ON_ALIGNED. On the other hand, to display grouping less frequently than the default, use MIN2
* or OFF. See the docs of each option for details.
*
* <p>
* Note: This enum specifies the strategy for grouping sizes. To set which character to use as the
* grouping separator, use the "symbols" setter.
*
* @stable ICU 63
*/
typedef enum UNumberGroupingStrategy {
/**
* Do not display grouping separators in any locale.
*
* @stable ICU 61
*/
UNUM_GROUPING_OFF,
/**
* Display grouping using locale defaults, except do not show grouping on values smaller than
* 10000 (such that there is a <em>minimum of two digits</em> before the first separator).
*
* <p>
* Note that locales may restrict grouping separators to be displayed only on 1 million or
* greater (for example, ee and hu) or disable grouping altogether (for example, bg currency).
*
* <p>
* Locale data is used to determine whether to separate larger numbers into groups of 2
* (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
*
* @stable ICU 61
*/
UNUM_GROUPING_MIN2,
/**
* Display grouping using the default strategy for all locales. This is the default behavior.
*
* <p>
* Note that locales may restrict grouping separators to be displayed only on 1 million or
* greater (for example, ee and hu) or disable grouping altogether (for example, bg currency).
*
* <p>
* Locale data is used to determine whether to separate larger numbers into groups of 2
* (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
*
* @stable ICU 61
*/
UNUM_GROUPING_AUTO,
/**
* Always display the grouping separator on values of at least 1000.
*
* <p>
* This option ignores the locale data that restricts or disables grouping, described in MIN2 and
* AUTO. This option may be useful to normalize the alignment of numbers, such as in a
* spreadsheet.
*
* <p>
* Locale data is used to determine whether to separate larger numbers into groups of 2
* (customary in South Asia) or groups of 3 (customary in Europe and the Americas).
*
* @stable ICU 61
*/
UNUM_GROUPING_ON_ALIGNED,
/**
* Use the Western defaults: groups of 3 and enabled for all numbers 1000 or greater. Do not use
* locale data for determining the grouping strategy.
*
* @stable ICU 61
*/
UNUM_GROUPING_THOUSANDS
#ifndef U_HIDE_INTERNAL_API
,
/**
* One more than the highest UNumberGroupingStrategy value.
*
* @internal ICU 62: The numeric value may change over time; see ICU ticket #12420.
*/
UNUM_GROUPING_COUNT
#endif /* U_HIDE_INTERNAL_API */
} UNumberGroupingStrategy;
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif //__UNUMBEROPTIONS_H__

View file

@ -0,0 +1,273 @@
// © 2022 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
#ifndef __USIMPLENUMBERFORMATTER_H__
#define __USIMPLENUMBERFORMATTER_H__
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/uformattednumber.h"
#include "unicode/unumberoptions.h"
/**
* \file
* \brief C API: Simple number formatting focused on low memory and code size.
*
* These functions render locale-aware number strings but without the bells and whistles found in
* other number formatting APIs such as those in unumberformatter.h, like units and currencies.
*
* <pre>
* LocalUSimpleNumberFormatterPointer uformatter(usnumf_openForLocale("de-CH", status));
* LocalUFormattedNumberPointer uresult(unumf_openResult(status));
* usnumf_formatInt64(uformatter.getAlias(), 55, uresult.getAlias(), status);
* assertEquals("",
* u"55",
* ufmtval_getString(unumf_resultAsValue(uresult.getAlias(), status), nullptr, status));
* </pre>
*/
#ifndef U_HIDE_DRAFT_API
/**
* An explicit sign option for a SimpleNumber.
*
* @draft ICU 73
*/
typedef enum USimpleNumberSign {
/**
* Render a plus sign.
*
* @draft ICU 73
*/
UNUM_SIMPLE_NUMBER_PLUS_SIGN,
/**
* Render no sign.
*
* @draft ICU 73
*/
UNUM_SIMPLE_NUMBER_NO_SIGN,
/**
* Render a minus sign.
*
* @draft ICU 73
*/
UNUM_SIMPLE_NUMBER_MINUS_SIGN,
} USimpleNumberSign;
struct USimpleNumber;
/**
* C-compatible version of icu::number::SimpleNumber.
*
* @draft ICU 73
*/
typedef struct USimpleNumber USimpleNumber;
struct USimpleNumberFormatter;
/**
* C-compatible version of icu::number::SimpleNumberFormatter.
*
* @draft ICU 73
*/
typedef struct USimpleNumberFormatter USimpleNumberFormatter;
/**
* Creates a new USimpleNumber to be formatted with a USimpleNumberFormatter.
*
* @draft ICU 73
*/
U_CAPI USimpleNumber* U_EXPORT2
usnum_openForInt64(int64_t value, UErrorCode* ec);
/**
* Changes the value of the USimpleNumber by a power of 10.
*
* This function immediately mutates the inner value.
*
* @draft ICU 73
*/
U_CAPI void U_EXPORT2
usnum_multiplyByPowerOfTen(USimpleNumber* unumber, int32_t power, UErrorCode* ec);
/**
* Rounds the value currently stored in the USimpleNumber to the given power of 10.
*
* This function immediately mutates the inner value.
*
* @draft ICU 73
*/
U_CAPI void U_EXPORT2
usnum_roundTo(USimpleNumber* unumber, int32_t power, UNumberFormatRoundingMode roundingMode, UErrorCode* ec);
/**
* Pads the beginning of the number with zeros up to the given minimum number of integer digits.
*
* This setting is applied upon formatting the number.
*
* @draft ICU 73
*/
U_CAPI void U_EXPORT2
usnum_setMinimumIntegerDigits(USimpleNumber* unumber, int32_t minimumIntegerDigits, UErrorCode* ec);
/**
* Pads the end of the number with zeros up to the given minimum number of fraction digits.
*
* This setting is applied upon formatting the number.
*
* @draft ICU 73
*/
U_CAPI void U_EXPORT2
usnum_setMinimumFractionDigits(USimpleNumber* unumber, int32_t minimumFractionDigits, UErrorCode* ec);
/**
* Truncates digits from the beginning of the number to the given maximum number of integer digits.
*
* This function immediately mutates the inner value.
*
* @draft ICU 73
*/
U_CAPI void U_EXPORT2
usnum_truncateStart(USimpleNumber* unumber, int32_t maximumIntegerDigits, UErrorCode* ec);
/**
* Sets the sign of the number: an explicit plus sign, explicit minus sign, or no sign.
*
* This setting is applied upon formatting the number.
*
* NOTE: This does not support accounting sign notation.
*
* @draft ICU 73
*/
U_CAPI void U_EXPORT2
usnum_setSign(USimpleNumber* unumber, USimpleNumberSign sign, UErrorCode* ec);
/**
* Creates a new USimpleNumberFormatter with all locale defaults.
*
* @draft ICU 73
*/
U_CAPI USimpleNumberFormatter* U_EXPORT2
usnumf_openForLocale(const char* locale, UErrorCode* ec);
/**
* Creates a new USimpleNumberFormatter, overriding the grouping strategy.
*
* @draft ICU 73
*/
U_CAPI USimpleNumberFormatter* U_EXPORT2
usnumf_openForLocaleAndGroupingStrategy(
const char* locale, UNumberGroupingStrategy groupingStrategy, UErrorCode* ec);
/**
* Formats a number using this SimpleNumberFormatter.
*
* The USimpleNumber is adopted and must not be freed after calling this function,
* even if the function sets an error code. If you use LocalUSimpleNumberPointer,
* call `.orphan()` when passing it to this function.
*
* @draft ICU 73
*/
U_CAPI void U_EXPORT2
usnumf_formatAndAdoptNumber(
const USimpleNumberFormatter* uformatter,
USimpleNumber* unumber,
UFormattedNumber* uresult,
UErrorCode* ec);
/**
* Formats an integer using this SimpleNumberFormatter.
*
* For more control over the formatting, use USimpleNumber.
*
* @draft ICU 73
*/
U_CAPI void U_EXPORT2
usnumf_formatInt64(
const USimpleNumberFormatter* uformatter,
int64_t value,
UFormattedNumber* uresult,
UErrorCode* ec);
/**
* Frees the memory held by a USimpleNumber.
*
* NOTE: Normally, a USimpleNumber should be adopted by usnumf_formatAndAdoptNumber.
*
* @draft ICU 73
*/
U_CAPI void U_EXPORT2
usnum_close(USimpleNumber* unumber);
/**
* Frees the memory held by a USimpleNumberFormatter.
*
* @draft ICU 73
*/
U_CAPI void U_EXPORT2
usnumf_close(USimpleNumberFormatter* uformatter);
#if U_SHOW_CPLUSPLUS_API
U_NAMESPACE_BEGIN
/**
* \class LocalUSimpleNumberPointer
* "Smart pointer" class; closes a USimpleNumber via usnum_close().
* For most methods see the LocalPointerBase base class.
*
* NOTE: Normally, a USimpleNumber should be adopted by usnumf_formatAndAdoptNumber.
* If you use LocalUSimpleNumberPointer, call `.orphan()` when passing to that function.
*
* Usage:
* <pre>
* LocalUSimpleNumberPointer uformatter(usnumf_openForInteger(...));
* // no need to explicitly call usnum_close()
* </pre>
*
* @see LocalPointerBase
* @see LocalPointer
* @draft ICU 73
*/
U_DEFINE_LOCAL_OPEN_POINTER(LocalUSimpleNumberPointer, USimpleNumber, usnum_close);
/**
* \class LocalUSimpleNumberFormatterPointer
* "Smart pointer" class; closes a USimpleNumberFormatter via usnumf_close().
* For most methods see the LocalPointerBase base class.
*
* Usage:
* <pre>
* LocalUSimpleNumberFormatterPointer uformatter(usnumf_openForLocale(...));
* // no need to explicitly call usnumf_close()
* </pre>
*
* @see LocalPointerBase
* @see LocalPointer
* @draft ICU 73
*/
U_DEFINE_LOCAL_OPEN_POINTER(LocalUSimpleNumberFormatterPointer, USimpleNumberFormatter, usnumf_close);
U_NAMESPACE_END
#endif // U_SHOW_CPLUSPLUS_API
#endif // U_HIDE_DRAFT_API
#endif /* #if !UCONFIG_NO_FORMATTING */
#endif //__USIMPLENUMBERFORMATTER_H__

View file

@ -1007,6 +1007,9 @@ group: numberformatter
number_scientific.o
currpinf.o
numrange_fluent.o numrange_impl.o
# NOTE: This could go into its own dependency block, but it would require
# refactoring more of the dependencies (e.g. removing class methods).
number_simple.o
deps
decnumber double_conversion formattable units unitsformatter
listformatter number_representation number_output

View file

@ -69,7 +69,8 @@ string_segment_test.o \
numbertest_parse.o numbertest_doubleconversion.o numbertest_skeletons.o \
static_unisets_test.o numfmtdatadriventest.o numbertest_range.o erarulestest.o \
formattedvaluetest.o formatted_string_builder_test.o numbertest_permutation.o \
units_data_test.o units_router_test.o units_test.o displayoptions_test.o
units_data_test.o units_router_test.o units_test.o displayoptions_test.o \
numbertest_simple.o
DEPS = $(OBJECTS:.o=.d)

View file

@ -177,6 +177,7 @@
<ClCompile Include="numbertest_doubleconversion.cpp" />
<ClCompile Include="numbertest_skeletons.cpp" />
<ClCompile Include="numbertest_range.cpp" />
<ClCompile Include="numbertest_simple.cpp" />
<ClCompile Include="numbertest_permutation.cpp" />
<ClCompile Include="numfmtst.cpp" />
<ClCompile Include="numfmtdatadriventest.cpp" />

View file

@ -298,6 +298,9 @@
<ClCompile Include="numbertest_range.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numbertest_simple.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numbertest_permutation.cpp">
<Filter>formatting</Filter>
</ClCompile>

View file

@ -365,6 +365,18 @@ class NumberRangeFormatterTest : public IntlTestWithFieldPosition {
const char16_t* expected);
};
class SimpleNumberFormatterTest : public IntlTestWithFieldPosition {
public:
void testBasic();
void testWithOptions();
void testSymbols();
void testSign();
void testCopyMove();
void testCAPI();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0) override;
};
class NumberPermutationTest : public IntlTest {
public:
void testPermutations();
@ -403,7 +415,8 @@ class NumberTest : public IntlTest {
TESTCLASS(7, NumberParserTest);
TESTCLASS(8, NumberSkeletonTest);
TESTCLASS(9, NumberRangeFormatterTest);
TESTCLASS(10, NumberPermutationTest);
TESTCLASS(10, SimpleNumberFormatterTest);
TESTCLASS(11, NumberPermutationTest);
default: name = ""; break; // needed to end loop
}
}

View file

@ -0,0 +1,206 @@
// © 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
#include <cmath>
#include "numbertest.h"
#include "unicode/simplenumberformatter.h"
void SimpleNumberFormatterTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
if (exec) {
logln("TestSuite SimpleNumberFormatterTest: ");
}
TESTCASE_AUTO_BEGIN;
TESTCASE_AUTO(testBasic);
TESTCASE_AUTO(testWithOptions);
TESTCASE_AUTO(testSymbols);
TESTCASE_AUTO(testSign);
TESTCASE_AUTO(testCopyMove);
TESTCASE_AUTO(testCAPI);
TESTCASE_AUTO_END;
}
void SimpleNumberFormatterTest::testBasic() {
IcuTestErrorCode status(*this, "testBasic");
SimpleNumberFormatter snf = SimpleNumberFormatter::forLocale("de-CH", status);
FormattedNumber result = snf.formatInt64(-1000007, status);
static const UFieldPosition expectedFieldPositions[] = {
// field, begin index, end index
{UNUM_SIGN_FIELD, 0, 1},
{UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
{UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
{UNUM_INTEGER_FIELD, 1, 10},
};
checkFormattedValue(
u"testBasic",
result,
u"-1000007",
UFIELD_CATEGORY_NUMBER,
expectedFieldPositions,
UPRV_LENGTHOF(expectedFieldPositions));
}
void SimpleNumberFormatterTest::testWithOptions() {
IcuTestErrorCode status(*this, "testWithOptions");
SimpleNumber num = SimpleNumber::forInt64(1250000, status);
num.setMinimumIntegerDigits(6, status);
num.setMinimumFractionDigits(2, status);
num.multiplyByPowerOfTen(-2, status);
num.roundTo(3, UNUM_ROUND_HALFUP, status);
num.truncateStart(4, status);
SimpleNumberFormatter snf = SimpleNumberFormatter::forLocale("bn", status);
FormattedNumber result = snf.format(std::move(num), status);
static const UFieldPosition expectedFieldPositions[] = {
// field, begin index, end index
{UNUM_GROUPING_SEPARATOR_FIELD, 1, 2},
{UNUM_GROUPING_SEPARATOR_FIELD, 4, 5},
{UNUM_INTEGER_FIELD, 0, 8},
{UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9},
{UNUM_FRACTION_FIELD, 9, 11},
};
checkFormattedValue(
u"testWithOptions",
result,
u",০৩,.",
UFIELD_CATEGORY_NUMBER,
expectedFieldPositions,
UPRV_LENGTHOF(expectedFieldPositions));
}
void SimpleNumberFormatterTest::testSymbols() {
IcuTestErrorCode status(*this, "testSymbols");
LocalPointer<DecimalFormatSymbols> symbols(new DecimalFormatSymbols("bn", status), status);
SimpleNumberFormatter snf = SimpleNumberFormatter::forLocaleAndSymbolsAndGroupingStrategy(
"en-US",
*symbols,
UNUM_GROUPING_ON_ALIGNED,
status
);
auto result = snf.formatInt64(987654321, status);
assertEquals("bn symbols with en-US pattern",
u"৯৮৭,৬৫৪,৩২১",
result.toTempString(status));
}
void SimpleNumberFormatterTest::testSign() {
IcuTestErrorCode status(*this, "testSign");
SimpleNumberFormatter snf = SimpleNumberFormatter::forLocale("und", status);
struct TestCase {
int64_t input;
USimpleNumberSign sign;
const char16_t* expected;
} cases[] = {
{ 1, UNUM_SIMPLE_NUMBER_NO_SIGN, u"1" },
{ 1, UNUM_SIMPLE_NUMBER_PLUS_SIGN, u"+1" },
{ 1, UNUM_SIMPLE_NUMBER_MINUS_SIGN, u"-1" },
{ 0, UNUM_SIMPLE_NUMBER_NO_SIGN, u"0" },
{ 0, UNUM_SIMPLE_NUMBER_PLUS_SIGN, u"+0" },
{ 0, UNUM_SIMPLE_NUMBER_MINUS_SIGN, u"-0" },
{ -1, UNUM_SIMPLE_NUMBER_NO_SIGN, u"1" },
{ -1, UNUM_SIMPLE_NUMBER_PLUS_SIGN, u"+1" },
{ -1, UNUM_SIMPLE_NUMBER_MINUS_SIGN, u"-1" },
};
for (auto& cas : cases) {
SimpleNumber num = SimpleNumber::forInt64(cas.input, status);
num.setSign(cas.sign, status);
auto result = snf.format(std::move(num), status);
assertEquals("", cas.expected, result.toTempString(status));
}
}
void SimpleNumberFormatterTest::testCopyMove() {
IcuTestErrorCode status(*this, "testCopyMove");
SimpleNumberFormatter snf0 = SimpleNumberFormatter::forLocale("und", status);
SimpleNumber sn0 = SimpleNumber::forInt64(55, status);
SimpleNumber sn1 = std::move(sn0);
snf0.format(std::move(sn0), status);
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR, "Use of moved number");
assertEquals("Move number constructor",
u"55",
snf0.format(std::move(sn1), status).toTempString(status));
SimpleNumber sn2;
snf0.format(std::move(sn2), status);
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR, "Default constructed number");
sn0 = SimpleNumber::forInt64(44, status);
assertEquals("Move number assignment",
u"44",
snf0.format(std::move(sn0), status).toTempString(status));
SimpleNumberFormatter snf1 = std::move(snf0);
snf0.format(SimpleNumber::forInt64(22, status), status);
status.expectErrorAndReset(U_INVALID_STATE_ERROR, "Use of moved formatter");
assertEquals("Move formatter constructor",
u"33",
snf1.format(SimpleNumber::forInt64(33, status), status).toTempString(status));
SimpleNumberFormatter snf2;
snf2.format(SimpleNumber::forInt64(22, status), status);
status.expectErrorAndReset(U_INVALID_STATE_ERROR, "Default constructed formatter");
snf0 = std::move(snf1);
assertEquals("Move formatter assignment",
u"22",
snf0.format(SimpleNumber::forInt64(22, status), status).toTempString(status));
snf0 = SimpleNumberFormatter::forLocale("de", status);
sn0 = SimpleNumber::forInt64(22, status);
sn0 = SimpleNumber::forInt64(11, status);
assertEquals("Move assignment with nonempty fields",
u"11",
snf0.format(std::move(sn0), status).toTempString(status));
}
void SimpleNumberFormatterTest::testCAPI() {
IcuTestErrorCode status(*this, "testCAPI");
LocalUSimpleNumberFormatterPointer uformatter(usnumf_openForLocale("de-CH", status));
LocalUFormattedNumberPointer uresult(unumf_openResult(status));
usnumf_formatInt64(uformatter.getAlias(), 55, uresult.getAlias(), status);
assertEquals("",
u"55",
ufmtval_getString(unumf_resultAsValue(uresult.getAlias(), status), nullptr, status));
LocalUSimpleNumberPointer unumber(usnum_openForInt64(44, status));
usnumf_formatAndAdoptNumber(uformatter.getAlias(), unumber.orphan(), uresult.getAlias(), status);
assertEquals("",
u"44",
ufmtval_getString(unumf_resultAsValue(uresult.getAlias(), status), nullptr, status));
unumber.adoptInstead(usnum_openForInt64(2335, status));
usnum_multiplyByPowerOfTen(unumber.getAlias(), -2, status);
usnum_roundTo(unumber.getAlias(), -1, UNUM_ROUND_HALFEVEN, status);
usnum_truncateStart(unumber.getAlias(), 1, status);
usnum_setMinimumFractionDigits(unumber.getAlias(), 3, status);
usnum_setMinimumIntegerDigits(unumber.getAlias(), 3, status);
usnumf_formatAndAdoptNumber(uformatter.getAlias(), unumber.orphan(), uresult.getAlias(), status);
assertEquals("",
u"003.400",
ufmtval_getString(unumf_resultAsValue(uresult.getAlias(), status), nullptr, status));
}
#endif