mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-14 17:24:01 +00:00
Merge pull request #5 from hugovdm/usage_glue
Implement Usage "Glue Code"
This commit is contained in:
commit
08132e7a33
24 changed files with 666 additions and 100 deletions
|
@ -323,11 +323,6 @@ public:
|
|||
* @return the array pointer
|
||||
*/
|
||||
T *getAlias() const { return ptr; }
|
||||
/**
|
||||
* Access without ownership change.
|
||||
* @return the array pointer
|
||||
*/
|
||||
const T *getConstAlias() const { return ptr; }
|
||||
/**
|
||||
* Returns the array limit. Simple convenience method.
|
||||
* @return getAlias()+getCapacity()
|
||||
|
@ -793,12 +788,12 @@ public:
|
|||
return this->fCount;
|
||||
}
|
||||
|
||||
T** getAlias() const {
|
||||
T** getAlias() {
|
||||
return this->fPool.getAlias();
|
||||
}
|
||||
|
||||
const T *const *getConstAlias() const {
|
||||
return this->fPool.getConstAlias();
|
||||
const T *const *getAlias() const {
|
||||
return this->fPool.getAlias();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -822,7 +817,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* Append all the items from another MaybeStackVector to this one.
|
||||
* Append copies of all the items from another MaybeStackVector to this one.
|
||||
*/
|
||||
void appendAll(const MaybeStackVector& other, UErrorCode& status) {
|
||||
for (int32_t i = 0; i < other.fCount; i++) {
|
||||
|
|
|
@ -25,7 +25,7 @@ class FormattedValueStringBuilderImpl;
|
|||
*
|
||||
* <ol>
|
||||
* <li>Efficient prepend as well as append.
|
||||
* <li>Keeps tracks of Fields in an efficient manner.
|
||||
* <li>Keeps track of Fields in an efficient manner.
|
||||
* </ol>
|
||||
*
|
||||
* See also FormattedValueStringBuilderImpl.
|
||||
|
|
|
@ -213,6 +213,7 @@
|
|||
<ClCompile Include="number_rounding.cpp" />
|
||||
<ClCompile Include="number_scientific.cpp" />
|
||||
<ClCompile Include="formatted_string_builder.cpp" />
|
||||
<ClCompile Include="number_usageprefs.cpp" />
|
||||
<ClCompile Include="number_utils.cpp" />
|
||||
<ClCompile Include="number_mapper.cpp" />
|
||||
<ClCompile Include="number_multiplier.cpp" />
|
||||
|
@ -485,6 +486,7 @@
|
|||
<ClInclude Include="number_scientific.h" />
|
||||
<ClInclude Include="formatted_string_builder.h" />
|
||||
<ClInclude Include="number_types.h" />
|
||||
<ClCompile Include="number_usageprefs.h" />
|
||||
<ClInclude Include="number_utypes.h" />
|
||||
<ClInclude Include="number_utils.h" />
|
||||
<ClInclude Include="number_mapper.h" />
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace impl {
|
|||
class DecNum;
|
||||
|
||||
/**
|
||||
* An class for representing a number to be processed by the decimal formatting pipeline. Includes
|
||||
* A class for representing a number to be processed by the decimal formatting pipeline. Includes
|
||||
* methods for rounding, plural rules, and decimal digit extraction.
|
||||
*
|
||||
* <p>By design, this is NOT IMMUTABLE and NOT THREAD SAFE. It is intended to be an intermediate
|
||||
|
@ -217,7 +217,13 @@ class U_I18N_API DecimalQuantity : public IFixedDecimal, public UMemory {
|
|||
|
||||
DecimalQuantity &setToDouble(double n);
|
||||
|
||||
/** decNumber is similar to BigDecimal in Java. */
|
||||
/**
|
||||
* Produces a DecimalQuantity that was parsed from a string by the decNumber
|
||||
* C Library.
|
||||
*
|
||||
* decNumber is similar to BigDecimal in Java, and supports parsing strings
|
||||
* such as "123.456621E+40".
|
||||
*/
|
||||
DecimalQuantity &setToDecNumber(StringPiece n, UErrorCode& status);
|
||||
|
||||
/** Internal method if the caller already has a DecNum. */
|
||||
|
|
|
@ -274,6 +274,20 @@ Derived NumberFormatterSettings<Derived>::scale(const Scale& scale)&& {
|
|||
return move;
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
Derived NumberFormatterSettings<Derived>::usage(const StringPiece usage) const& {
|
||||
Derived copy(*this);
|
||||
copy.fMacros.usage.set(usage);
|
||||
return copy;
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
Derived NumberFormatterSettings<Derived>::usage(const StringPiece usage)&& {
|
||||
Derived move(std::move(*this));
|
||||
move.fMacros.usage.set(usage);
|
||||
return move;
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
Derived NumberFormatterSettings<Derived>::padding(const Padder& padder) const& {
|
||||
Derived copy(*this);
|
||||
|
@ -702,9 +716,9 @@ LocalizedNumberFormatter::formatDecimalQuantity(const DecimalQuantity& dq, UErro
|
|||
|
||||
void LocalizedNumberFormatter::formatImpl(impl::UFormattedNumberData* results, UErrorCode& status) const {
|
||||
if (computeCompiled(status)) {
|
||||
fCompiled->format(results->quantity, results->getStringRef(), status);
|
||||
fCompiled->format(results, status);
|
||||
} else {
|
||||
NumberFormatterImpl::formatStatic(fMacros, results->quantity, results->getStringRef(), status);
|
||||
NumberFormatterImpl::formatStatic(fMacros, results, status);
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
|
|
|
@ -32,13 +32,16 @@ NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& s
|
|||
: NumberFormatterImpl(macros, true, status) {
|
||||
}
|
||||
|
||||
int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue,
|
||||
FormattedStringBuilder& outString, UErrorCode& status) {
|
||||
int32_t NumberFormatterImpl::formatStatic(const MacroProps ¯os, UFormattedNumberData *results,
|
||||
UErrorCode &status) {
|
||||
DecimalQuantity &inValue = results->quantity;
|
||||
FormattedStringBuilder &outString = results->getStringRef();
|
||||
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);
|
||||
length += writeAffixes(micros, outString, 0, length, status);
|
||||
results->outputUnit = std::move(micros.outputUnit);
|
||||
return length;
|
||||
}
|
||||
|
||||
|
@ -54,13 +57,15 @@ int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Sig
|
|||
// The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation.
|
||||
// See MicroProps::processQuantity() for details.
|
||||
|
||||
int32_t NumberFormatterImpl::format(DecimalQuantity& inValue, FormattedStringBuilder& outString,
|
||||
UErrorCode& status) const {
|
||||
int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &status) const {
|
||||
DecimalQuantity &inValue = results->quantity;
|
||||
FormattedStringBuilder &outString = results->getStringRef();
|
||||
MicroProps micros;
|
||||
preProcess(inValue, micros, status);
|
||||
if (U_FAILURE(status)) { return 0; }
|
||||
int32_t length = writeNumber(micros, inValue, outString, 0, status);
|
||||
length += writeAffixes(micros, outString, 0, length, status);
|
||||
results->outputUnit = std::move(micros.outputUnit);
|
||||
return length;
|
||||
}
|
||||
|
||||
|
@ -222,6 +227,19 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
|||
/// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Unit Preferences and Conversions as our first step
|
||||
if (macros.usage.isSet()) {
|
||||
if (!isCldrUnit) {
|
||||
// We only support "usage" when the input unit is a CLDR Unit.
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
auto usagePrefsHandler =
|
||||
new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fUsage, chain, status);
|
||||
fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status);
|
||||
chain = fUsagePrefsHandler.getAlias();
|
||||
}
|
||||
|
||||
// Multiplier
|
||||
if (macros.scale.isValid()) {
|
||||
fMicros.helpers.multiplier.setAndChain(macros.scale, chain);
|
||||
|
@ -330,7 +348,8 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
|||
patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status);
|
||||
}
|
||||
if (safe) {
|
||||
fImmutablePatternModifier.adoptInstead(patternModifier->createImmutable(status));
|
||||
fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status),
|
||||
status);
|
||||
}
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
|
@ -338,24 +357,26 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
|||
|
||||
// Outer modifier (CLDR units and currency long names)
|
||||
if (isCldrUnit) {
|
||||
fLongNameHandler.adoptInstead(
|
||||
LongNameHandler::forMeasureUnit(
|
||||
macros.locale,
|
||||
macros.unit,
|
||||
macros.perUnit,
|
||||
unitWidth,
|
||||
resolvePluralRules(macros.rules, macros.locale, status),
|
||||
chain,
|
||||
status));
|
||||
chain = fLongNameHandler.getAlias();
|
||||
if (macros.usage.isSet()) {
|
||||
fLongNameMultiplexer.adoptInsteadAndCheckErrorCode(
|
||||
LongNameMultiplexer::forMeasureUnits(
|
||||
macros.locale, *fUsagePrefsHandler->getOutputUnits(), unitWidth,
|
||||
resolvePluralRules(macros.rules, macros.locale, status), chain, status),
|
||||
status);
|
||||
chain = fLongNameMultiplexer.getAlias();
|
||||
} else {
|
||||
fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status);
|
||||
LongNameHandler::forMeasureUnit(macros.locale, macros.unit, macros.perUnit, unitWidth,
|
||||
resolvePluralRules(macros.rules, macros.locale, status),
|
||||
chain, fLongNameHandler.getAlias(), status);
|
||||
chain = fLongNameHandler.getAlias();
|
||||
}
|
||||
} else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
|
||||
fLongNameHandler.adoptInstead(
|
||||
LongNameHandler::forCurrencyLongNames(
|
||||
macros.locale,
|
||||
currency,
|
||||
resolvePluralRules(macros.rules, macros.locale, status),
|
||||
chain,
|
||||
status));
|
||||
fLongNameHandler.adoptInsteadAndCheckErrorCode(
|
||||
LongNameHandler::forCurrencyLongNames(
|
||||
macros.locale, currency, resolvePluralRules(macros.rules, macros.locale, status), chain,
|
||||
status),
|
||||
status);
|
||||
chain = fLongNameHandler.getAlias();
|
||||
} else {
|
||||
// No outer modifier required
|
||||
|
@ -379,6 +400,9 @@ NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe,
|
|||
safe,
|
||||
chain,
|
||||
status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (newCompactHandler == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
#include "number_types.h"
|
||||
#include "formatted_string_builder.h"
|
||||
#include "number_patternstring.h"
|
||||
#include "number_usageprefs.h"
|
||||
#include "number_utils.h"
|
||||
#include "number_patternmodifier.h"
|
||||
#include "number_longnames.h"
|
||||
#include "number_compact.h"
|
||||
#include "number_microprops.h"
|
||||
#include "number_utypes.h"
|
||||
|
||||
U_NAMESPACE_BEGIN namespace number {
|
||||
namespace impl {
|
||||
|
@ -34,9 +36,8 @@ class NumberFormatterImpl : public UMemory {
|
|||
/**
|
||||
* Builds and evaluates an "unsafe" MicroPropsGenerator, which is cheaper but can be used only once.
|
||||
*/
|
||||
static int32_t
|
||||
formatStatic(const MacroProps ¯os, DecimalQuantity &inValue, FormattedStringBuilder &outString,
|
||||
UErrorCode &status);
|
||||
static int32_t formatStatic(const MacroProps ¯os, UFormattedNumberData *results,
|
||||
UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Prints only the prefix and suffix; used for DecimalFormat getters.
|
||||
|
@ -51,7 +52,7 @@ class NumberFormatterImpl : public UMemory {
|
|||
/**
|
||||
* Evaluates the "safe" MicroPropsGenerator created by "fromMacros".
|
||||
*/
|
||||
int32_t format(DecimalQuantity& inValue, FormattedStringBuilder& outString, UErrorCode& status) const;
|
||||
int32_t format(UFormattedNumberData *results, UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Like format(), but saves the result into an output MicroProps without additional processing.
|
||||
|
@ -82,7 +83,9 @@ class NumberFormatterImpl : public UMemory {
|
|||
int32_t end, UErrorCode& status);
|
||||
|
||||
private:
|
||||
// Head of the MicroPropsGenerator linked list:
|
||||
// Head of the MicroPropsGenerator linked list. Subclasses' processQuantity
|
||||
// methods process this list in a parent-first order, such that the last
|
||||
// item added, which this points to, typically has its logic executed last.
|
||||
const MicroPropsGenerator *fMicroPropsGenerator = nullptr;
|
||||
|
||||
// Tail of the list:
|
||||
|
@ -90,13 +93,15 @@ class NumberFormatterImpl : public UMemory {
|
|||
|
||||
// Other fields possibly used by the number formatting pipeline:
|
||||
// TODO: Convert more of these LocalPointers to value objects to reduce the number of news?
|
||||
LocalPointer<const UsagePrefsHandler> fUsagePrefsHandler;
|
||||
LocalPointer<const DecimalFormatSymbols> fSymbols;
|
||||
LocalPointer<const PluralRules> fRules;
|
||||
LocalPointer<const ParsedPatternInfo> fPatternInfo;
|
||||
LocalPointer<const ScientificHandler> fScientificHandler;
|
||||
LocalPointer<MutablePatternModifier> fPatternModifier;
|
||||
LocalPointer<ImmutablePatternModifier> fImmutablePatternModifier;
|
||||
LocalPointer<const LongNameHandler> fLongNameHandler;
|
||||
LocalPointer<LongNameHandler> fLongNameHandler;
|
||||
LocalPointer<const LongNameMultiplexer> fLongNameMultiplexer;
|
||||
LocalPointer<const CompactHandler> fCompactHandler;
|
||||
|
||||
// Value objects possibly used by the number formatting pipeline:
|
||||
|
|
|
@ -22,7 +22,17 @@ using namespace icu::number::impl;
|
|||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Display Name (this format has no placeholder).
|
||||
*
|
||||
* Used as an index into the LongNameHandler::simpleFormats array. Units
|
||||
* resources cover the normal set of PluralRules keys, as well as `dnam` and
|
||||
* `per` forms.
|
||||
*/
|
||||
constexpr int32_t DNAM_INDEX = StandardPlural::Form::COUNT;
|
||||
/**
|
||||
* @copydoc DNAM_INDEX
|
||||
*/
|
||||
constexpr int32_t PER_INDEX = StandardPlural::Form::COUNT + 1;
|
||||
constexpr int32_t ARRAY_LENGTH = StandardPlural::Form::COUNT + 2;
|
||||
|
||||
|
@ -87,6 +97,12 @@ class PluralTableSink : public ResourceSink {
|
|||
|
||||
// NOTE: outArray MUST have room for all StandardPlural values. No bounds checking is performed.
|
||||
|
||||
// Populates outArray with `locale`-specific values for `unit` through use of
|
||||
// PluralTableSink, reading from resources *unitsNarrow* and *unitsShort* (for
|
||||
// width UNUM_UNIT_WIDTH_NARROW), or just *unitsShort* (for width
|
||||
// UNUM_UNIT_WIDTH_SHORT). For other widths, it would read just "units".
|
||||
//
|
||||
// outArray must be of fixed length ARRAY_LENGTH.
|
||||
void getMeasureData(const Locale &locale, const MeasureUnit &unit, const UNumberUnitWidth &width,
|
||||
UnicodeString *outArray, UErrorCode &status) {
|
||||
PluralTableSink sink(outArray);
|
||||
|
@ -184,14 +200,19 @@ UnicodeString getPerUnitFormat(const Locale& locale, const UNumberUnitWidth &wid
|
|||
|
||||
} // namespace
|
||||
|
||||
LongNameHandler*
|
||||
LongNameHandler::forMeasureUnit(const Locale &loc, const MeasureUnit &unitRef, const MeasureUnit &perUnit,
|
||||
const UNumberUnitWidth &width, const PluralRules *rules,
|
||||
const MicroPropsGenerator *parent, UErrorCode &status) {
|
||||
// TODO(units,hugovdm): deal properly with "perUnit" parameter here:
|
||||
void LongNameHandler::forMeasureUnit(const Locale &loc, const MeasureUnit &unitRef,
|
||||
const MeasureUnit &perUnit, const UNumberUnitWidth &width,
|
||||
const PluralRules *rules, const MicroPropsGenerator *parent,
|
||||
LongNameHandler *fillIn, UErrorCode &status) {
|
||||
if (fillIn == nullptr) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
if (uprv_strlen(unitRef.getType()) == 0 || uprv_strlen(perUnit.getType()) == 0) {
|
||||
// TODO(ICU-20941): Unsanctioned unit. Not yet fully supported. Set an error code.
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
MeasureUnit unit = unitRef;
|
||||
|
@ -203,59 +224,75 @@ LongNameHandler::forMeasureUnit(const Locale &loc, const MeasureUnit &unitRef, c
|
|||
unit = resolved;
|
||||
} else {
|
||||
// No simplified form is available.
|
||||
return forCompoundUnit(loc, unit, perUnit, width, rules, parent, status);
|
||||
forCompoundUnit(loc, unit, perUnit, width, rules, parent, fillIn, status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto* result = new LongNameHandler(rules, parent);
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
}
|
||||
UnicodeString simpleFormats[ARRAY_LENGTH];
|
||||
getMeasureData(loc, unit, width, simpleFormats, status);
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
result->simpleFormatsToModifiers(simpleFormats, {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status);
|
||||
return result;
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
fillIn->rules = rules;
|
||||
fillIn->parent = parent;
|
||||
fillIn->simpleFormatsToModifiers(simpleFormats, {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD},
|
||||
status);
|
||||
}
|
||||
|
||||
LongNameHandler*
|
||||
LongNameHandler::forCompoundUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
|
||||
const UNumberUnitWidth &width, const PluralRules *rules,
|
||||
const MicroPropsGenerator *parent, UErrorCode &status) {
|
||||
auto* result = new LongNameHandler(rules, parent);
|
||||
if (result == nullptr) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return nullptr;
|
||||
// TODO(units,hugovdm): deal properly with "perUnit" parameter here:
|
||||
void LongNameHandler::forCompoundUnit(const Locale &loc, const MeasureUnit &unit,
|
||||
const MeasureUnit &perUnit, const UNumberUnitWidth &width,
|
||||
const PluralRules *rules, const MicroPropsGenerator *parent,
|
||||
LongNameHandler *fillIn, UErrorCode &status) {
|
||||
if (fillIn == nullptr) {
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return;
|
||||
}
|
||||
UnicodeString primaryData[ARRAY_LENGTH];
|
||||
getMeasureData(loc, unit, width, primaryData, status);
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
UnicodeString secondaryData[ARRAY_LENGTH];
|
||||
getMeasureData(loc, perUnit, width, secondaryData, status);
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UnicodeString perUnitFormat;
|
||||
if (!secondaryData[PER_INDEX].isBogus()) {
|
||||
perUnitFormat = secondaryData[PER_INDEX];
|
||||
} else {
|
||||
UnicodeString rawPerUnitFormat = getPerUnitFormat(loc, width, status);
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
// rawPerUnitFormat is something like "{0}/{1}"; we need to substitute in the secondary unit.
|
||||
SimpleFormatter compiled(rawPerUnitFormat, 2, 2, status);
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
UnicodeString secondaryFormat = getWithPlural(secondaryData, StandardPlural::Form::ONE, status);
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
// Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale.
|
||||
SimpleFormatter secondaryCompiled(secondaryFormat, 0, 1, status);
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
UnicodeString secondaryString = secondaryCompiled.getTextWithNoArguments().trim();
|
||||
// TODO: Why does UnicodeString need to be explicit in the following line?
|
||||
compiled.format(UnicodeString(u"{0}"), secondaryString, perUnitFormat, status);
|
||||
if (U_FAILURE(status)) { return result; }
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
result->multiSimpleFormatsToModifiers(primaryData, perUnitFormat, {UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status);
|
||||
return result;
|
||||
fillIn->rules = rules;
|
||||
fillIn->parent = parent;
|
||||
fillIn->multiSimpleFormatsToModifiers(primaryData, perUnitFormat,
|
||||
{UFIELD_CATEGORY_NUMBER, UNUM_MEASURE_UNIT_FIELD}, status);
|
||||
}
|
||||
|
||||
UnicodeString LongNameHandler::getUnitDisplayName(
|
||||
|
@ -338,7 +375,9 @@ void LongNameHandler::multiSimpleFormatsToModifiers(const UnicodeString *leadFor
|
|||
|
||||
void LongNameHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs,
|
||||
UErrorCode &status) const {
|
||||
parent->processQuantity(quantity, micros, status);
|
||||
if (parent != NULL) {
|
||||
parent->processQuantity(quantity, micros, status);
|
||||
}
|
||||
StandardPlural::Form pluralForm = utils::getPluralSafe(micros.rounder, rules, quantity, status);
|
||||
micros.modOuter = &fModifiers[pluralForm];
|
||||
}
|
||||
|
@ -347,4 +386,49 @@ const Modifier* LongNameHandler::getModifier(Signum /*signum*/, StandardPlural::
|
|||
return &fModifiers[plural];
|
||||
}
|
||||
|
||||
LongNameMultiplexer *
|
||||
LongNameMultiplexer::forMeasureUnits(const Locale &loc, const MaybeStackVector<MeasureUnit> &units,
|
||||
const UNumberUnitWidth &width, const PluralRules *rules,
|
||||
const MicroPropsGenerator *parent, UErrorCode &status) {
|
||||
LocalPointer<LongNameMultiplexer> result(new LongNameMultiplexer(parent), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
U_ASSERT(units.length() > 0);
|
||||
result->fMeasureUnits.adoptInstead(new MeasureUnit[units.length()]);
|
||||
for (int32_t i = 0, length = units.length(); i < length; i++) {
|
||||
// Create empty new LongNameHandler:
|
||||
LongNameHandler *lnh =
|
||||
result->fLongNameHandlers.emplaceBackAndCheckErrorCode(status);
|
||||
result->fMeasureUnits[i] = *units[i];
|
||||
// Fill in LongNameHandler:
|
||||
LongNameHandler::forMeasureUnit(loc, *units[i],
|
||||
MeasureUnit(), // TODO(units): deal with COMPOUND and MIXED units
|
||||
width, rules, NULL, lnh, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return result.orphan();
|
||||
}
|
||||
|
||||
void LongNameMultiplexer::processQuantity(DecimalQuantity &quantity, MicroProps µs,
|
||||
UErrorCode &status) const {
|
||||
// We call parent->processQuantity() from the Multiplexer, instead of
|
||||
// letting LongNameHandler handle it: we don't know which LongNameHandler to
|
||||
// call until we've called the parent!
|
||||
fParent->processQuantity(quantity, micros, status);
|
||||
|
||||
// Call the correct LongNameHandler based on outputUnit
|
||||
for (int i = 0; i < fLongNameHandlers.length(); i++) {
|
||||
if (fMeasureUnits[i] == micros.outputUnit) {
|
||||
fLongNameHandlers[i]->processQuantity(quantity, micros, status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// We shouldn't receive any outputUnit for which we haven't already got a
|
||||
// LongNameHandler:
|
||||
status = U_INTERNAL_PROGRAM_ERROR;
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef __NUMBER_LONGNAMES_H__
|
||||
#define __NUMBER_LONGNAMES_H__
|
||||
|
||||
#include "cmemory.h"
|
||||
#include "unicode/uversion.h"
|
||||
#include "number_utils.h"
|
||||
#include "number_modifiers.h"
|
||||
|
@ -33,10 +34,10 @@ class LongNameHandler : public MicroPropsGenerator, public ModifierStore, public
|
|||
forCurrencyLongNames(const Locale &loc, const CurrencyUnit ¤cy, const PluralRules *rules,
|
||||
const MicroPropsGenerator *parent, UErrorCode &status);
|
||||
|
||||
static LongNameHandler*
|
||||
forMeasureUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
|
||||
const UNumberUnitWidth &width, const PluralRules *rules,
|
||||
const MicroPropsGenerator *parent, UErrorCode &status);
|
||||
static void forMeasureUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
|
||||
const UNumberUnitWidth &width, const PluralRules *rules,
|
||||
const MicroPropsGenerator *parent, LongNameHandler *fillIn,
|
||||
UErrorCode &status);
|
||||
|
||||
void
|
||||
processQuantity(DecimalQuantity &quantity, MicroProps µs, UErrorCode &status) const U_OVERRIDE;
|
||||
|
@ -45,22 +46,63 @@ class LongNameHandler : public MicroPropsGenerator, public ModifierStore, public
|
|||
|
||||
private:
|
||||
SimpleModifier fModifiers[StandardPlural::Form::COUNT];
|
||||
// Not owned
|
||||
const PluralRules *rules;
|
||||
// Not owned
|
||||
const MicroPropsGenerator *parent;
|
||||
|
||||
LongNameHandler(const PluralRules *rules, const MicroPropsGenerator *parent)
|
||||
: rules(rules), parent(parent) {}
|
||||
: rules(rules), parent(parent) {
|
||||
}
|
||||
|
||||
static LongNameHandler*
|
||||
forCompoundUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
|
||||
const UNumberUnitWidth &width, const PluralRules *rules,
|
||||
const MicroPropsGenerator *parent, UErrorCode &status);
|
||||
LongNameHandler() : rules(nullptr), parent(nullptr) {
|
||||
}
|
||||
|
||||
friend class MemoryPool<LongNameHandler>; // To enable emplaceBack();
|
||||
friend class NumberFormatterImpl;
|
||||
|
||||
static void forCompoundUnit(const Locale &loc, const MeasureUnit &unit, const MeasureUnit &perUnit,
|
||||
const UNumberUnitWidth &width, const PluralRules *rules,
|
||||
const MicroPropsGenerator *parent, LongNameHandler *fillIn,
|
||||
UErrorCode &status);
|
||||
|
||||
void simpleFormatsToModifiers(const UnicodeString *simpleFormats, Field field, UErrorCode &status);
|
||||
void multiSimpleFormatsToModifiers(const UnicodeString *leadFormats, UnicodeString trailFormat,
|
||||
Field field, UErrorCode &status);
|
||||
};
|
||||
|
||||
const int MAX_PREFS_COUNT = 10;
|
||||
|
||||
/**
|
||||
* A MicroPropsGenerator that multiplexes between different LongNameHandlers,
|
||||
* depending on the outputUnit (micros.helpers.outputUnit should be set earlier
|
||||
* in the chain).
|
||||
*/
|
||||
class LongNameMultiplexer : public MicroPropsGenerator, public UMemory {
|
||||
public:
|
||||
// FIXME: docstring?
|
||||
static LongNameMultiplexer *forMeasureUnits(const Locale &loc,
|
||||
const MaybeStackVector<MeasureUnit> &units,
|
||||
const UNumberUnitWidth &width, const PluralRules *rules,
|
||||
const MicroPropsGenerator *parent, UErrorCode &status);
|
||||
|
||||
void processQuantity(DecimalQuantity &quantity, MicroProps µs,
|
||||
UErrorCode &status) const U_OVERRIDE;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Because we only know which LongNameHandler we wish to call after calling
|
||||
* earlier MicroPropsGenerators in the chain, LongNameMultiplexer keeps the
|
||||
* parent link, while the LongNameHandlers are given no parents.
|
||||
*/
|
||||
MaybeStackVector<LongNameHandler> fLongNameHandlers;
|
||||
LocalArray<MeasureUnit> fMeasureUnits;
|
||||
const MicroPropsGenerator *fParent;
|
||||
|
||||
LongNameMultiplexer(const MicroPropsGenerator *parent) : fParent(parent) {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
} // namespace number
|
||||
U_NAMESPACE_END
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
U_NAMESPACE_BEGIN namespace number {
|
||||
namespace impl {
|
||||
|
||||
// TODO(units): generated by MicroPropsGenerator, but inherits from it too. Do
|
||||
// we want to better document why? There's an explanation for processQuantity:
|
||||
// * As MicroProps is the "base instance", this implementation of
|
||||
// * MicoPropsGenerator::processQuantity() just ensures that the output
|
||||
// * `micros` is correctly initialized.
|
||||
struct MicroProps : public MicroPropsGenerator {
|
||||
|
||||
// NOTE: All of these fields are properly initialized in NumberFormatterImpl.
|
||||
|
@ -49,6 +54,8 @@ struct MicroProps : public MicroPropsGenerator {
|
|||
MultiplierFormatHandler multiplier;
|
||||
} helpers;
|
||||
|
||||
// The MeasureUnit with which the output measurement is represented.
|
||||
MeasureUnit outputUnit;
|
||||
|
||||
MicroProps() = default;
|
||||
|
||||
|
@ -56,7 +63,27 @@ struct MicroProps : public MicroPropsGenerator {
|
|||
|
||||
MicroProps& operator=(const MicroProps& other) = default;
|
||||
|
||||
void processQuantity(DecimalQuantity&, MicroProps& micros, UErrorCode& status) const U_OVERRIDE {
|
||||
/**
|
||||
* As MicroProps is the "base instance", this implementation of
|
||||
* MicoPropsGenerator::processQuantity() just ensures that the output
|
||||
* `micros` is correctly initialized.
|
||||
*
|
||||
* For the "safe" invocation of this function, micros must not be *this,
|
||||
* such that a copy of the base instance is made. For the "unsafe" path,
|
||||
* this function can be used only once, because the base MicroProps instance
|
||||
* will be modified and thus not be available for re-use.
|
||||
*
|
||||
* TODO(units,hugovdm): deal with outputUnits: processQuantity may need to
|
||||
* return a MeasurementUnit instance too, in some fashion. Or do we just
|
||||
* keep it in micros.outputUnit?
|
||||
*
|
||||
* @param quantity The quantity for consideration and optional mutation.
|
||||
* @param micros The MicroProps instance to populate. If this parameter is
|
||||
* not already `*this`, it will be overwritten with a copy of `*this`.
|
||||
*/
|
||||
void processQuantity(DecimalQuantity &quantity, MicroProps µs,
|
||||
UErrorCode &status) const U_OVERRIDE {
|
||||
(void) quantity;
|
||||
(void) status;
|
||||
if (this == µs) {
|
||||
// Unsafe path: no need to perform a copy.
|
||||
|
@ -65,12 +92,14 @@ struct MicroProps : public MicroPropsGenerator {
|
|||
U_ASSERT(exhausted);
|
||||
} else {
|
||||
// Safe path: copy self into the output micros.
|
||||
U_ASSERT(!exhausted);
|
||||
micros = *this;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Internal fields:
|
||||
// FIXME: describe?
|
||||
bool exhausted = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "unicode/measunit.h"
|
||||
#include "unicode/numberformatter.h"
|
||||
#include "number_utypes.h"
|
||||
#include "util.h"
|
||||
|
@ -19,6 +20,8 @@ UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumber)
|
|||
|
||||
#define UPRV_NOARG
|
||||
|
||||
// TODO(units,hugovdm): see if there's Unit Usage Formatting consequences here?
|
||||
// Ensure tests are thorough, check rounding etc.
|
||||
void FormattedNumber::toDecimalNumber(ByteSink& sink, UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG)
|
||||
impl::DecNum decnum;
|
||||
|
@ -32,6 +35,11 @@ void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpi
|
|||
fData->getAllFieldPositions(fpih, status);
|
||||
}
|
||||
|
||||
MeasureUnit FormattedNumber::getOutputUnit(UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(MeasureUnit())
|
||||
return fData->outputUnit;
|
||||
}
|
||||
|
||||
void FormattedNumber::getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const {
|
||||
UPRV_FORMATTED_VALUE_METHOD_GUARD(UPRV_NOARG)
|
||||
output = fData->quantity;
|
||||
|
|
|
@ -246,16 +246,17 @@ class U_I18N_API ModifierStore {
|
|||
* itself. The {@link #processQuantity} method performs the final step in the number processing pipeline: it uses the
|
||||
* quantity to generate a finalized {@link MicroProps}, which can be used to render the number to output.
|
||||
*
|
||||
* <p>
|
||||
* In other words, this interface is used for the parts of number processing that are <em>quantity-dependent</em>.
|
||||
*
|
||||
* <p>
|
||||
* In order to allow for multiple different objects to all mutate the same MicroProps, a "chain" of MicroPropsGenerators
|
||||
* are linked together, and each one is responsible for manipulating a certain quantity-dependent part of the
|
||||
* MicroProps. At the tail of the linked list is a base instance of {@link MicroProps} with properties that are not
|
||||
* quantity-dependent. Each element in the linked list calls {@link #processQuantity} on its "parent", then does its
|
||||
* work, and then returns the result.
|
||||
*
|
||||
* This chain of MicroPropsGenerators is typically constructed by NumberFormatterImpl::macrosToMicroGenerator() when
|
||||
* constructing a NumberFormatter.
|
||||
*
|
||||
* Exported as U_I18N_API because it is a base class for other exported types
|
||||
*
|
||||
*/
|
||||
|
@ -264,13 +265,12 @@ class U_I18N_API MicroPropsGenerator {
|
|||
virtual ~MicroPropsGenerator();
|
||||
|
||||
/**
|
||||
* Considers the given {@link DecimalQuantity}, optionally mutates it, and returns a {@link MicroProps}.
|
||||
* Considers the given {@link DecimalQuantity}, optionally mutates it, and
|
||||
* populates a {@link MicroProps} instance.
|
||||
*
|
||||
* @param quantity
|
||||
* The quantity for consideration and optional mutation.
|
||||
* @param micros
|
||||
* The MicroProps instance to populate.
|
||||
* @return A MicroProps instance resolved for the quantity.
|
||||
* @param quantity The quantity for consideration and optional mutation.
|
||||
* @param micros The MicroProps instance to populate. It will be modified as
|
||||
* needed for the given quantity.
|
||||
*/
|
||||
virtual void processQuantity(DecimalQuantity& quantity, MicroProps& micros,
|
||||
UErrorCode& status) const = 0;
|
||||
|
|
113
icu4c/source/i18n/number_usageprefs.cpp
Normal file
113
icu4c/source/i18n/number_usageprefs.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
// © 2020 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "number_usageprefs.h"
|
||||
#include "number_decimalquantity.h"
|
||||
#include "number_microprops.h"
|
||||
#include "unicode/numberformatter.h"
|
||||
|
||||
using namespace icu::number;
|
||||
using namespace icu::number::impl;
|
||||
|
||||
// Copy constructor
|
||||
Usage::Usage(const Usage &other) : fUsage(nullptr), fLength(other.fLength), fError(other.fError) {
|
||||
if (other.fUsage != nullptr) {
|
||||
fUsage = (char *)uprv_malloc(fLength + 1);
|
||||
uprv_strncpy(fUsage, other.fUsage, fLength + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy assignment operator
|
||||
Usage &Usage::operator=(const Usage &other) {
|
||||
fLength = other.fLength;
|
||||
if (other.fUsage != nullptr) {
|
||||
fUsage = (char *)uprv_malloc(fLength + 1);
|
||||
uprv_strncpy(fUsage, other.fUsage, fLength + 1);
|
||||
}
|
||||
fError = other.fError;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Move constructor - can it be improved by taking over src's "this" instead of
|
||||
// copying contents? Swapping pointers makes sense for heap objects but not for
|
||||
// stack objects.
|
||||
// *this = std::move(src);
|
||||
Usage::Usage(Usage &&src) U_NOEXCEPT : fUsage(src.fUsage), fLength(src.fLength), fError(src.fError) {
|
||||
// Take ownership away from src if necessary
|
||||
src.fUsage = nullptr;
|
||||
}
|
||||
|
||||
// Move assignment operator
|
||||
Usage &Usage::operator=(Usage &&src) U_NOEXCEPT {
|
||||
if (this == &src) {
|
||||
return *this;
|
||||
}
|
||||
if (fUsage != nullptr) {
|
||||
uprv_free(fUsage);
|
||||
}
|
||||
fUsage = src.fUsage;
|
||||
fLength = src.fLength;
|
||||
fError = src.fError;
|
||||
// Take ownership away from src if necessary
|
||||
src.fUsage = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Usage::~Usage() {
|
||||
if (fUsage != nullptr) {
|
||||
uprv_free(fUsage);
|
||||
fUsage = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Usage::set(StringPiece value) {
|
||||
if (fUsage != nullptr) {
|
||||
uprv_free(fUsage);
|
||||
fUsage = nullptr;
|
||||
}
|
||||
fLength = value.length();
|
||||
fUsage = (char *)uprv_malloc(fLength + 1);
|
||||
uprv_strncpy(fUsage, value.data(), fLength);
|
||||
fUsage[fLength] = 0;
|
||||
}
|
||||
|
||||
UsagePrefsHandler::UsagePrefsHandler(const Locale &locale,
|
||||
const MeasureUnit inputUnit,
|
||||
const StringPiece usage,
|
||||
const MicroPropsGenerator *parent,
|
||||
UErrorCode &status)
|
||||
: fUnitsRouter(inputUnit, StringPiece(locale.getCountry()), usage, status),
|
||||
fParent(parent) {
|
||||
}
|
||||
|
||||
void UsagePrefsHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs,
|
||||
UErrorCode &status) const {
|
||||
fParent->processQuantity(quantity, micros, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
quantity.roundToInfinity(); // Enables toDouble
|
||||
auto routed = fUnitsRouter.route(quantity.toDouble(), status);
|
||||
micros.outputUnit = routed[0]->getUnit();
|
||||
quantity.setToDouble(routed[0]->getNumber().getDouble());
|
||||
|
||||
// TODO(units): here we are always overriding Precision. (1) get precision
|
||||
// from fUnitsRouter, (2) ensure we use the UnitPreference skeleton's
|
||||
// precision only when there isn't an explicit override we prefer to use.
|
||||
// This needs to be handled within
|
||||
// NumberFormatterImpl::macrosToMicroGenerator in number_formatimpl.cpp
|
||||
Precision precision = Precision::integer().withMinDigits(2);
|
||||
UNumberFormatRoundingMode roundingMode;
|
||||
// Temporary until ICU 64?
|
||||
roundingMode = precision.fRoundingMode;
|
||||
CurrencyUnit currency(u"", status);
|
||||
micros.rounder = {precision, roundingMode, currency, status};
|
||||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
53
icu4c/source/i18n/number_usageprefs.h
Normal file
53
icu4c/source/i18n/number_usageprefs.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
// © 2020 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
#ifndef __NUMBER_USAGEPREFS_H__
|
||||
#define __NUMBER_USAGEPREFS_H__
|
||||
|
||||
#include "cmemory.h"
|
||||
#include "number_types.h"
|
||||
#include "unitsrouter.h"
|
||||
|
||||
U_NAMESPACE_BEGIN namespace number {
|
||||
namespace impl {
|
||||
|
||||
/**
|
||||
* A MicroPropsGenerator which uses UnitsRouter to produce output converted to a
|
||||
* MeasureUnit appropriate for a particular localized usage: see
|
||||
* NumberFormatterSettings::usage().
|
||||
*/
|
||||
class U_I18N_API UsagePrefsHandler : public MicroPropsGenerator, public UMemory {
|
||||
public:
|
||||
UsagePrefsHandler(const Locale &locale, const MeasureUnit inputUnit, const StringPiece usage,
|
||||
const MicroPropsGenerator *parent, UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Obtains the appropriate output value, MeasurementUnit and
|
||||
* rounding/precision behaviour from the UnitsRouter.
|
||||
*/
|
||||
void processQuantity(DecimalQuantity &quantity, MicroProps µs,
|
||||
UErrorCode &status) const U_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Returns the list of possible output units, i.e. the full set of
|
||||
* preferences, for the localized, usage-specific unit preferences.
|
||||
*
|
||||
* The returned pointer should be valid for the lifetime of the
|
||||
* UsagePrefsHandler instance.
|
||||
*/
|
||||
const MaybeStackVector<MeasureUnit> *getOutputUnits() const {
|
||||
return fUnitsRouter.getOutputUnits();
|
||||
}
|
||||
|
||||
private:
|
||||
UnitsRouter fUnitsRouter;
|
||||
const MicroPropsGenerator *fParent;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
} // namespace number
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif // __NUMBER_USAGEPREFS_H__
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
|
@ -28,9 +28,6 @@ const DecimalQuantity* validateUFormattedNumberToDecimalQuantity(
|
|||
* This struct is held internally by the C++ version FormattedNumber since the member types are not
|
||||
* declared in the public header file.
|
||||
*
|
||||
* The DecimalQuantity is not currently being used by FormattedNumber, but at some point it could be used
|
||||
* to add a toDecNumber() or similar method.
|
||||
*
|
||||
* Exported as U_I18N_API for tests
|
||||
*/
|
||||
class U_I18N_API UFormattedNumberData : public FormattedValueStringBuilderImpl {
|
||||
|
@ -38,7 +35,13 @@ public:
|
|||
UFormattedNumberData() : FormattedValueStringBuilderImpl(kUndefinedField) {}
|
||||
virtual ~UFormattedNumberData();
|
||||
|
||||
// The formatted quantity.
|
||||
DecimalQuantity quantity;
|
||||
|
||||
// The output unit for the formatted quantity.
|
||||
// TODO(units,hugovdm): populate this correctly - it's not properly
|
||||
// implemented yet.
|
||||
MeasureUnit outputUnit;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -123,6 +123,7 @@ number_patternstring.cpp
|
|||
number_rounding.cpp
|
||||
number_scientific.cpp
|
||||
number_skeletons.cpp
|
||||
number_usageprefs.cpp
|
||||
number_utils.cpp
|
||||
numfmt.cpp
|
||||
numparse_affixes.cpp
|
||||
|
|
|
@ -158,6 +158,7 @@ struct UFormattedNumberImpl;
|
|||
class MutablePatternModifier;
|
||||
class ImmutablePatternModifier;
|
||||
struct DecimalFormatWarehouse;
|
||||
class UsagePrefsHandler;
|
||||
|
||||
/**
|
||||
* Used for NumberRangeFormatter and implemented in numrange_fluent.cpp.
|
||||
|
@ -767,6 +768,11 @@ class U_I18N_API Precision : public UMemory {
|
|||
|
||||
// To allow access to the skeleton generation code:
|
||||
friend class impl::GeneratorHelpers;
|
||||
|
||||
// TODO(units): revisit when UnitsRouter is changed: do we still need this
|
||||
// once Precision is returned by UnitsRouter? For now, we allow access to
|
||||
// Precision constructor from UsagePrefsHandler:
|
||||
friend class impl::UsagePrefsHandler;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1127,6 +1133,56 @@ class U_I18N_API Scale : public UMemory {
|
|||
|
||||
namespace impl {
|
||||
|
||||
// Do not enclose entire Usage with #ifndef U_HIDE_INTERNAL_API, needed for a protected field
|
||||
/**
|
||||
* Manages NumberFormatterSettings::usage()'s char* instance on the heap.
|
||||
* @internal
|
||||
*/
|
||||
class U_I18N_API Usage : public UMemory {
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
|
||||
public:
|
||||
/** @internal */
|
||||
Usage(const Usage& other);
|
||||
|
||||
/** @internal */
|
||||
Usage& operator=(const Usage& other);
|
||||
|
||||
/** @internal */
|
||||
Usage(Usage &&src) U_NOEXCEPT;
|
||||
|
||||
/** @internal */
|
||||
Usage& operator=(Usage&& src) U_NOEXCEPT;
|
||||
|
||||
/** @internal */
|
||||
~Usage();
|
||||
|
||||
/** @internal */
|
||||
int16_t length() const { return fLength; }
|
||||
|
||||
/** @internal */
|
||||
void set(StringPiece value);
|
||||
|
||||
/** @internal */
|
||||
bool isSet() const { return fLength > 0; }
|
||||
|
||||
private:
|
||||
char *fUsage;
|
||||
int16_t fLength;
|
||||
UErrorCode fError;
|
||||
|
||||
Usage() : fUsage(nullptr), fLength(0), fError(U_ZERO_ERROR) {}
|
||||
|
||||
// Allow NumberFormatterImpl to access fUsage.
|
||||
friend class impl::NumberFormatterImpl;
|
||||
|
||||
// Allow MacroProps/MicroProps to initialize empty instances.
|
||||
friend struct impl::MacroProps;
|
||||
|
||||
#endif // U_HIDE_INTERNAL_API
|
||||
};
|
||||
|
||||
// Do not enclose entire SymbolsWrapper with #ifndef U_HIDE_INTERNAL_API, needed for a protected field
|
||||
/** @internal */
|
||||
class U_I18N_API SymbolsWrapper : public UMemory {
|
||||
|
@ -1410,6 +1466,9 @@ struct U_I18N_API MacroProps : public UMemory {
|
|||
/** @internal */
|
||||
Scale scale; // = Scale(); (benign value)
|
||||
|
||||
/** @internal */
|
||||
Usage usage; // = Usage(); (no usage)
|
||||
|
||||
/** @internal */
|
||||
const AffixPatternProvider* affixProvider = nullptr; // no ownership
|
||||
|
||||
|
@ -1599,6 +1658,9 @@ class U_I18N_API NumberFormatterSettings {
|
|||
*
|
||||
* If a per-unit is specified without a primary unit via {@link #unit}, the behavior is undefined.
|
||||
*
|
||||
* TODO(units): add proper support for COMPOUND and MIXED units.
|
||||
* Specify behaviour here, test intended behaviour...
|
||||
*
|
||||
* @param perUnit
|
||||
* The unit to render in the denominator.
|
||||
* @return The fluent chain
|
||||
|
@ -1610,6 +1672,9 @@ class U_I18N_API NumberFormatterSettings {
|
|||
/**
|
||||
* Overload of perUnit() for use on an rvalue reference.
|
||||
*
|
||||
* TODO(units): add proper support for COMPOUND and MIXED units.
|
||||
* Specify behaviour here, test intended behaviour...
|
||||
*
|
||||
* @param perUnit
|
||||
* The unit to render in the denominator.
|
||||
* @return The fluent chain.
|
||||
|
@ -1624,6 +1689,9 @@ class U_I18N_API NumberFormatterSettings {
|
|||
*
|
||||
* Note: consider using the MeasureFormat factory methods that return by value.
|
||||
*
|
||||
* TODO(units): add proper support for COMPOUND and MIXED units.
|
||||
* Specify behaviour here, test intended behaviour...
|
||||
*
|
||||
* @param perUnit
|
||||
* The unit to render in the denominator.
|
||||
* @return The fluent chain.
|
||||
|
@ -1636,6 +1704,9 @@ class U_I18N_API NumberFormatterSettings {
|
|||
/**
|
||||
* Overload of adoptPerUnit() for use on an rvalue reference.
|
||||
*
|
||||
* TODO(units): add proper support for COMPOUND and MIXED units.
|
||||
* Specify behaviour here, test intended behaviour...
|
||||
*
|
||||
* @param perUnit
|
||||
* The unit to render in the denominator.
|
||||
* @return The fluent chain.
|
||||
|
@ -2073,6 +2144,15 @@ class U_I18N_API NumberFormatterSettings {
|
|||
* Setting usage to an empty string clears the usage (disables usage-based
|
||||
* localized formatting).
|
||||
*
|
||||
* Setting a usage string but not a correct input unit will result in an
|
||||
* U_ILLEGAL_ARGUMENT_ERROR.
|
||||
*
|
||||
* TODO(units): When setting both usage and rounding/precision behaviour via
|
||||
* NumberFormatterSetter, we think we want the latter to override any
|
||||
* skeleton in the UnitPreferences. Add unit tests to demontrate desired
|
||||
* behaviour, fix macrosToMicroGenerator to handle this correctly, and
|
||||
* update this documentation.
|
||||
*
|
||||
* @param usage A `usage` parameter from the units resource. See the
|
||||
* unitPreferenceData in *source/data/misc/units.txt*, generated from
|
||||
* `unitPreferenceData` in [CLDR's
|
||||
|
|
|
@ -410,7 +410,7 @@ void U_I18N_API UnitPreferences::getPreferencesFor(StringPiece category, StringP
|
|||
if (U_FAILURE(status)) { return; }
|
||||
U_ASSERT(idx >= 0); // Failures should have been taken care of by `status`.
|
||||
const UnitPreferenceMetadata *m = metadata_[idx];
|
||||
outPreferences = unitPrefs_.getConstAlias() + m->prefsOffset;
|
||||
outPreferences = unitPrefs_.getAlias() + m->prefsOffset;
|
||||
preferenceCount = m->prefsCount;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece
|
|||
return;
|
||||
}
|
||||
|
||||
outputUnits_.emplaceBackAndCheckErrorCode(status, complexTargetUnit);
|
||||
converterPreferences_.emplaceBackAndCheckErrorCode(status, inputUnit, complexTargetUnit,
|
||||
preference.geq, conversionRates, status);
|
||||
if (U_FAILURE(status)) {
|
||||
|
@ -49,7 +50,7 @@ UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece
|
|||
}
|
||||
}
|
||||
|
||||
MaybeStackVector<Measure> UnitsRouter::route(double quantity, UErrorCode &status) {
|
||||
MaybeStackVector<Measure> UnitsRouter::route(double quantity, UErrorCode &status) const {
|
||||
for (int i = 0, n = converterPreferences_.length(); i < n; i++) {
|
||||
const auto &converterPreference = *converterPreferences_[i];
|
||||
|
||||
|
@ -63,6 +64,10 @@ MaybeStackVector<Measure> UnitsRouter::route(double quantity, UErrorCode &status
|
|||
return lastConverter.convert(quantity, status);
|
||||
}
|
||||
|
||||
const MaybeStackVector<MeasureUnit> *UnitsRouter::getOutputUnits() const {
|
||||
return &outputUnits_;
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -75,9 +75,21 @@ class U_I18N_API UnitsRouter {
|
|||
public:
|
||||
UnitsRouter(MeasureUnit inputUnit, StringPiece locale, StringPiece usage, UErrorCode &status);
|
||||
|
||||
MaybeStackVector<Measure> route(double quantity, UErrorCode &status);
|
||||
MaybeStackVector<Measure> route(double quantity, UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Returns the list of possible output units, i.e. the full set of
|
||||
* preferences, for the localized, usage-specific unit preferences.
|
||||
*
|
||||
* The returned pointer should be valid for the lifetime of the
|
||||
* UnitsRouter instance.
|
||||
*/
|
||||
const MaybeStackVector<MeasureUnit> *getOutputUnits() const;
|
||||
|
||||
private:
|
||||
// List of possible output units
|
||||
MaybeStackVector<MeasureUnit> outputUnits_;
|
||||
|
||||
MaybeStackVector<ConverterPreference> converterPreferences_;
|
||||
};
|
||||
|
||||
|
|
|
@ -979,6 +979,8 @@ group: number_output
|
|||
number_representation format formatted_value_sbimpl
|
||||
# PluralRules internals:
|
||||
unifiedcache
|
||||
# Unit Formatting
|
||||
units
|
||||
|
||||
group: numberformatter
|
||||
# ICU 60+ NumberFormatter API
|
||||
|
@ -990,11 +992,11 @@ group: numberformatter
|
|||
number_mapper.o number_modifiers.o number_multiplier.o
|
||||
number_notation.o number_padding.o
|
||||
number_patternmodifier.o number_patternstring.o number_rounding.o
|
||||
number_scientific.o
|
||||
number_scientific.o number_usageprefs.o
|
||||
currpinf.o dcfmtsym.o numsys.o
|
||||
numrange_fluent.o numrange_impl.o
|
||||
deps
|
||||
decnumber double_conversion formattable units
|
||||
decnumber double_conversion formattable units unitsformatter
|
||||
number_representation number_output
|
||||
uclean_i18n common
|
||||
|
||||
|
|
|
@ -35,6 +35,10 @@ typedef struct UFieldPositionWithCategory {
|
|||
|
||||
class IntlTestWithFieldPosition : public IntlTest {
|
||||
public:
|
||||
// Tests FormattedValue's toString, toTempString, and nextPosition methods.
|
||||
//
|
||||
// expectedCategory gets combined with expectedFieldPositions to call
|
||||
// checkMixedFormattedValue.
|
||||
void checkFormattedValue(
|
||||
const char16_t* message,
|
||||
const FormattedValue& fv,
|
||||
|
@ -43,6 +47,7 @@ public:
|
|||
const UFieldPosition* expectedFieldPositions,
|
||||
int32_t length);
|
||||
|
||||
// Tests FormattedValue's toString, toTempString, and nextPosition methods.
|
||||
void checkMixedFormattedValue(
|
||||
const char16_t* message,
|
||||
const FormattedValue& fv,
|
||||
|
|
|
@ -54,6 +54,7 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
|
|||
void notationCompact();
|
||||
void unitMeasure();
|
||||
void unitCompoundMeasure();
|
||||
void unitUsage();
|
||||
void unitCurrency();
|
||||
void unitPercent();
|
||||
void percentParity();
|
||||
|
|
|
@ -73,6 +73,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
|
|||
TESTCASE_AUTO(notationCompact);
|
||||
TESTCASE_AUTO(unitMeasure);
|
||||
TESTCASE_AUTO(unitCompoundMeasure);
|
||||
TESTCASE_AUTO(unitUsage);
|
||||
TESTCASE_AUTO(unitCurrency);
|
||||
TESTCASE_AUTO(unitPercent);
|
||||
if (!quick) {
|
||||
|
@ -674,6 +675,87 @@ void NumberFormatterApiTest::unitMeasure() {
|
|||
u"5 a\u00F1os");
|
||||
}
|
||||
|
||||
void NumberFormatterApiTest::unitUsage() {
|
||||
UnlocalizedNumberFormatter unloc_formatter =
|
||||
NumberFormatter::with().usage("road").unit(MeasureUnit::getMeter());
|
||||
|
||||
IcuTestErrorCode status(*this, "unitUsage()");
|
||||
|
||||
LocalizedNumberFormatter formatter = unloc_formatter.locale("en-ZA");
|
||||
FormattedNumber formattedNum = formatter.formatDouble(300, status);
|
||||
assertTrue(UnicodeString("unitUsage() en-ZA road, got outputUnit: \"") +
|
||||
formattedNum.getOutputUnit(status).getIdentifier() + "\"",
|
||||
MeasureUnit::getMeter() == formattedNum.getOutputUnit(status));
|
||||
assertEquals("unitUsage() en-ZA road", "300 m", formattedNum.toString(status));
|
||||
// TODO(units,hugovdm): this works with "assertUndefinedSkeleton(f);"
|
||||
// commented out. Design skeletons ("measure-unit/length-meter usage/road"?)
|
||||
// then use this:
|
||||
// assertFormatDescendingBig(u"unitUsage() en-ZA road",
|
||||
// nullptr,
|
||||
// nullptr,
|
||||
// unloc_formatter,
|
||||
// Locale("en-ZA"),
|
||||
// u"87\u00A0650 km",
|
||||
// u"8\u00A0765 km",
|
||||
// u"877 km",
|
||||
// u"88 km",
|
||||
// u"8,8 km",
|
||||
// u"877 m",
|
||||
// u"88 m",
|
||||
// u"8,8 m",
|
||||
// u"0 m");
|
||||
|
||||
formatter = unloc_formatter.locale("en-GB");
|
||||
formattedNum = formatter.formatDouble(300, status);
|
||||
assertTrue(UnicodeString("unitUsage() en-GB road, got outputUnit: \"") +
|
||||
formattedNum.getOutputUnit(status).getIdentifier() + "\"",
|
||||
MeasureUnit::getYard() == formattedNum.getOutputUnit(status));
|
||||
assertEquals("unitUsage() en-GB road", "328 yd", formattedNum.toString(status));
|
||||
// TODO(units,hugovdm): this works with "assertUndefinedSkeleton(f);"
|
||||
// commented out. Design skeletons ("measure-unit/length-meter usage/road"?)
|
||||
// then use this:
|
||||
// assertFormatDescendingBig(u"unitUsage() en-GB road",
|
||||
// nullptr,
|
||||
// nullptr,
|
||||
// unloc_formatter,
|
||||
// Locale("en-GB"),
|
||||
// u"54,463 mi",
|
||||
// u"5,446 mi",
|
||||
// u"545 mi",
|
||||
// u"54 mi",
|
||||
// u"5.4 mi",
|
||||
// u"0.54 mi",
|
||||
// u"96 yd",
|
||||
// u"9.6 yd",
|
||||
// u"0 yd");
|
||||
|
||||
formatter = unloc_formatter.locale("en-US");
|
||||
formattedNum = formatter.formatDouble(300, status);
|
||||
assertTrue(UnicodeString("unitUsage() en-US road, got outputUnit: \"") +
|
||||
formattedNum.getOutputUnit(status).getIdentifier() + "\"",
|
||||
MeasureUnit::getFoot() == formattedNum.getOutputUnit(status));
|
||||
assertEquals("unitUsage() en-US road", "984 ft", formattedNum.toString(status));
|
||||
// TODO(units,hugovdm): this works with "assertUndefinedSkeleton(f);"
|
||||
// commented out. Design skeletons ("measure-unit/length-meter usage/road"?)
|
||||
// then use this:
|
||||
// assertFormatDescendingBig(u"unitUsage() en-US road",
|
||||
// nullptr,
|
||||
// nullptr,
|
||||
// unloc_formatter,
|
||||
// Locale("en-US"),
|
||||
// u"54,463 mi",
|
||||
// u"5,446 mi",
|
||||
// u"545 mi",
|
||||
// u"54 mi",
|
||||
// u"5.4 mi",
|
||||
// u"0.54 mi",
|
||||
// u"288 ft",
|
||||
// u"29 ft",
|
||||
// u"0 ft");
|
||||
|
||||
// TODO(hugovdm): consider fixing TODO(ICU-20941) too?
|
||||
}
|
||||
|
||||
void NumberFormatterApiTest::unitCompoundMeasure() {
|
||||
assertFormatDescending(
|
||||
u"Meters Per Second Short (unit that simplifies) and perUnit method",
|
||||
|
|
Loading…
Add table
Reference in a new issue