// © 2018 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html #ifndef __NUMBERRANGEFORMATTER_H__ #define __NUMBERRANGEFORMATTER_H__ #include "unicode/utypes.h" #if U_SHOW_CPLUSPLUS_API #if !UCONFIG_NO_FORMATTING #include #include "unicode/appendable.h" #include "unicode/fieldpos.h" #include "unicode/formattedvalue.h" #include "unicode/fpositer.h" #include "unicode/numberformatter.h" /** * \file * \brief C++ API: Library for localized formatting of number, currency, and unit ranges. * * The main entrypoint to the formatting of ranges of numbers, including currencies and other units of measurement. *

* Usage example: *

*

 * NumberRangeFormatter::with()
 *     .identityFallback(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE)
 *     .numberFormatterFirst(NumberFormatter::with().adoptUnit(MeasureUnit::createMeter()))
 *     .numberFormatterSecond(NumberFormatter::with().adoptUnit(MeasureUnit::createKilometer()))
 *     .locale("en-GB")
 *     .formatRange(750, 1.2, status)
 *     .toString(status);
 * // => "750 m - 1.2 km"
 * 
*

* Like NumberFormatter, NumberRangeFormatter instances (i.e., LocalizedNumberRangeFormatter * and UnlocalizedNumberRangeFormatter) are immutable and thread-safe. This API is based on the * fluent design pattern popularized by libraries such as Google's Guava. * * @author Shane Carr */ /** * Defines how to merge fields that are identical across the range sign. * * @stable ICU 63 */ typedef enum UNumberRangeCollapse { /** * Use locale data and heuristics to determine how much of the string to collapse. Could end up collapsing none, * some, or all repeated pieces in a locale-sensitive way. * * The heuristics used for this option are subject to change over time. * * @stable ICU 63 */ UNUM_RANGE_COLLAPSE_AUTO, /** * Do not collapse any part of the number. Example: "3.2 thousand kilograms – 5.3 thousand kilograms" * * @stable ICU 63 */ UNUM_RANGE_COLLAPSE_NONE, /** * Collapse the unit part of the number, but not the notation, if present. Example: "3.2 thousand – 5.3 thousand * kilograms" * * @stable ICU 63 */ UNUM_RANGE_COLLAPSE_UNIT, /** * Collapse any field that is equal across the range sign. May introduce ambiguity on the magnitude of the * number. Example: "3.2 – 5.3 thousand kilograms" * * @stable ICU 63 */ UNUM_RANGE_COLLAPSE_ALL } UNumberRangeCollapse; /** * Defines the behavior when the two numbers in the range are identical after rounding. To programmatically detect * when the identity fallback is used, compare the lower and upper BigDecimals via FormattedNumber. * * @stable ICU 63 * @see NumberRangeFormatter */ typedef enum UNumberRangeIdentityFallback { /** * Show the number as a single value rather than a range. Example: "$5" * * @stable ICU 63 */ UNUM_IDENTITY_FALLBACK_SINGLE_VALUE, /** * Show the number using a locale-sensitive approximation pattern. If the numbers were the same before rounding, * show the single value. Example: "~$5" or "$5" * * @stable ICU 63 */ UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE, /** * Show the number using a locale-sensitive approximation pattern. Use the range pattern always, even if the * inputs are the same. Example: "~$5" * * @stable ICU 63 */ UNUM_IDENTITY_FALLBACK_APPROXIMATELY, /** * Show the number as the range of two equal values. Use the range pattern always, even if the inputs are the * same. Example (with RangeCollapse.NONE): "$5 – $5" * * @stable ICU 63 */ UNUM_IDENTITY_FALLBACK_RANGE } UNumberRangeIdentityFallback; /** * Used in the result class FormattedNumberRange to indicate to the user whether the numbers formatted in the range * were equal or not, and whether or not the identity fallback was applied. * * @stable ICU 63 * @see NumberRangeFormatter */ typedef enum UNumberRangeIdentityResult { /** * Used to indicate that the two numbers in the range were equal, even before any rounding rules were applied. * * @stable ICU 63 * @see NumberRangeFormatter */ UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING, /** * Used to indicate that the two numbers in the range were equal, but only after rounding rules were applied. * * @stable ICU 63 * @see NumberRangeFormatter */ UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING, /** * Used to indicate that the two numbers in the range were not equal, even after rounding rules were applied. * * @stable ICU 63 * @see NumberRangeFormatter */ UNUM_IDENTITY_RESULT_NOT_EQUAL, #ifndef U_HIDE_INTERNAL_API /** * The number of entries in this enum. * @internal */ UNUM_IDENTITY_RESULT_COUNT #endif } UNumberRangeIdentityResult; U_NAMESPACE_BEGIN namespace number { // icu::number // Forward declarations: class UnlocalizedNumberRangeFormatter; class LocalizedNumberRangeFormatter; class FormattedNumberRange; namespace impl { // Forward declarations: struct RangeMacroProps; class DecimalQuantity; class UFormattedNumberRangeData; class NumberRangeFormatterImpl; } // namespace impl /** * \cond * Export an explicit template instantiation. See datefmt.h * (When building DLLs for Windows this is required.) */ #if U_PLATFORM == U_PF_WINDOWS && !defined(U_IN_DOXYGEN) } // namespace icu::number U_NAMESPACE_END template struct U_I18N_API std::atomic< U_NAMESPACE_QUALIFIER number::impl::NumberRangeFormatterImpl*>; U_NAMESPACE_BEGIN namespace number { // icu::number #endif /** \endcond */ // Other helper classes would go here, but there are none. namespace impl { // icu::number::impl // Do not enclose entire MacroProps with #ifndef U_HIDE_INTERNAL_API, needed for a protected field /** @internal */ struct U_I18N_API RangeMacroProps : public UMemory { /** @internal */ UnlocalizedNumberFormatter formatter1; // = NumberFormatter::with(); /** @internal */ UnlocalizedNumberFormatter formatter2; // = NumberFormatter::with(); /** @internal */ bool singleFormatter = true; /** @internal */ UNumberRangeCollapse collapse = UNUM_RANGE_COLLAPSE_AUTO; /** @internal */ UNumberRangeIdentityFallback identityFallback = UNUM_IDENTITY_FALLBACK_APPROXIMATELY; /** @internal */ Locale locale; // NOTE: Uses default copy and move constructors. /** * Check all members for errors. * @internal */ bool copyErrorTo(UErrorCode &status) const { return formatter1.copyErrorTo(status) || formatter2.copyErrorTo(status); } }; } // namespace impl /** * An abstract base class for specifying settings related to number formatting. This class is implemented by * {@link UnlocalizedNumberRangeFormatter} and {@link LocalizedNumberRangeFormatter}. This class is not intended for * public subclassing. */ template class U_I18N_API NumberRangeFormatterSettings { public: /** * Sets the NumberFormatter instance to use for the numbers in the range. The same formatter is applied to both * sides of the range. *

* The NumberFormatter instances must not have a locale applied yet; the locale specified on the * NumberRangeFormatter will be used. * * @param formatter * The formatter to use for both numbers in the range. * @return The fluent chain. * @stable ICU 63 */ Derived numberFormatterBoth(const UnlocalizedNumberFormatter &formatter) const &; /** * Overload of numberFormatterBoth() for use on an rvalue reference. * * @param formatter * The formatter to use for both numbers in the range. * @return The fluent chain. * @see #numberFormatterBoth * @stable ICU 63 */ Derived numberFormatterBoth(const UnlocalizedNumberFormatter &formatter) &&; /** * Overload of numberFormatterBoth() for use on an rvalue reference. * * @param formatter * The formatter to use for both numbers in the range. * @return The fluent chain. * @see #numberFormatterBoth * @stable ICU 63 */ Derived numberFormatterBoth(UnlocalizedNumberFormatter &&formatter) const &; /** * Overload of numberFormatterBoth() for use on an rvalue reference. * * @param formatter * The formatter to use for both numbers in the range. * @return The fluent chain. * @see #numberFormatterBoth * @stable ICU 63 */ Derived numberFormatterBoth(UnlocalizedNumberFormatter &&formatter) &&; /** * Sets the NumberFormatter instance to use for the first number in the range. *

* The NumberFormatter instances must not have a locale applied yet; the locale specified on the * NumberRangeFormatter will be used. * * @param formatterFirst * The formatter to use for the first number in the range. * @return The fluent chain. * @stable ICU 63 */ Derived numberFormatterFirst(const UnlocalizedNumberFormatter &formatterFirst) const &; /** * Overload of numberFormatterFirst() for use on an rvalue reference. * * @param formatterFirst * The formatter to use for the first number in the range. * @return The fluent chain. * @see #numberFormatterFirst * @stable ICU 63 */ Derived numberFormatterFirst(const UnlocalizedNumberFormatter &formatterFirst) &&; /** * Overload of numberFormatterFirst() for use on an rvalue reference. * * @param formatterFirst * The formatter to use for the first number in the range. * @return The fluent chain. * @see #numberFormatterFirst * @stable ICU 63 */ Derived numberFormatterFirst(UnlocalizedNumberFormatter &&formatterFirst) const &; /** * Overload of numberFormatterFirst() for use on an rvalue reference. * * @param formatterFirst * The formatter to use for the first number in the range. * @return The fluent chain. * @see #numberFormatterFirst * @stable ICU 63 */ Derived numberFormatterFirst(UnlocalizedNumberFormatter &&formatterFirst) &&; /** * Sets the NumberFormatter instance to use for the second number in the range. *

* The NumberFormatter instances must not have a locale applied yet; the locale specified on the * NumberRangeFormatter will be used. * * @param formatterSecond * The formatter to use for the second number in the range. * @return The fluent chain. * @stable ICU 63 */ Derived numberFormatterSecond(const UnlocalizedNumberFormatter &formatterSecond) const &; /** * Overload of numberFormatterSecond() for use on an rvalue reference. * * @param formatterSecond * The formatter to use for the second number in the range. * @return The fluent chain. * @see #numberFormatterSecond * @stable ICU 63 */ Derived numberFormatterSecond(const UnlocalizedNumberFormatter &formatterSecond) &&; /** * Overload of numberFormatterSecond() for use on an rvalue reference. * * @param formatterSecond * The formatter to use for the second number in the range. * @return The fluent chain. * @see #numberFormatterSecond * @stable ICU 63 */ Derived numberFormatterSecond(UnlocalizedNumberFormatter &&formatterSecond) const &; /** * Overload of numberFormatterSecond() for use on an rvalue reference. * * @param formatterSecond * The formatter to use for the second number in the range. * @return The fluent chain. * @see #numberFormatterSecond * @stable ICU 63 */ Derived numberFormatterSecond(UnlocalizedNumberFormatter &&formatterSecond) &&; /** * Sets the aggressiveness of "collapsing" fields across the range separator. Possible values: *

*

*

* The default value is AUTO. * * @param collapse * The collapsing strategy to use for this range. * @return The fluent chain. * @stable ICU 63 */ Derived collapse(UNumberRangeCollapse collapse) const &; /** * Overload of collapse() for use on an rvalue reference. * * @param collapse * The collapsing strategy to use for this range. * @return The fluent chain. * @see #collapse * @stable ICU 63 */ Derived collapse(UNumberRangeCollapse collapse) &&; /** * Sets the behavior when the two sides of the range are the same. This could happen if the same two numbers are * passed to the formatRange function, or if different numbers are passed to the function but they become the same * after rounding rules are applied. Possible values: *

*

*

* The default value is APPROXIMATELY. * * @param identityFallback * The strategy to use when formatting two numbers that end up being the same. * @return The fluent chain. * @stable ICU 63 */ Derived identityFallback(UNumberRangeIdentityFallback identityFallback) const &; /** * Overload of identityFallback() for use on an rvalue reference. * * @param identityFallback * The strategy to use when formatting two numbers that end up being the same. * @return The fluent chain. * @see #identityFallback * @stable ICU 63 */ Derived identityFallback(UNumberRangeIdentityFallback identityFallback) &&; /** * Returns the current (Un)LocalizedNumberRangeFormatter as a LocalPointer * wrapping a heap-allocated copy of the current object. * * This is equivalent to new-ing the move constructor with a value object * as the argument. * * @return A wrapped (Un)LocalizedNumberRangeFormatter pointer, or a wrapped * nullptr on failure. * @stable ICU 64 */ LocalPointer clone() const &; /** * Overload of clone for use on an rvalue reference. * * @return A wrapped (Un)LocalizedNumberRangeFormatter pointer, or a wrapped * nullptr on failure. * @stable ICU 64 */ LocalPointer clone() &&; /** * Sets the UErrorCode if an error occurred in the fluent chain. * Preserves older error codes in the outErrorCode. * @return TRUE if U_FAILURE(outErrorCode) * @stable ICU 63 */ UBool copyErrorTo(UErrorCode &outErrorCode) const { if (U_FAILURE(outErrorCode)) { // Do not overwrite the older error code return TRUE; } fMacros.copyErrorTo(outErrorCode); return U_FAILURE(outErrorCode); } // NOTE: Uses default copy and move constructors. private: impl::RangeMacroProps fMacros; // Don't construct me directly! Use (Un)LocalizedNumberFormatter. NumberRangeFormatterSettings() = default; friend class LocalizedNumberRangeFormatter; friend class UnlocalizedNumberRangeFormatter; }; /** * A NumberRangeFormatter that does not yet have a locale. In order to format, a locale must be specified. * * Instances of this class are immutable and thread-safe. * * @see NumberRangeFormatter * @stable ICU 63 */ class U_I18N_API UnlocalizedNumberRangeFormatter : public NumberRangeFormatterSettings, public UMemory { public: /** * Associate the given locale with the number range formatter. The locale is used for picking the * appropriate symbols, formats, and other data for number display. * * @param locale * The locale to use when loading data for number formatting. * @return The fluent chain. * @stable ICU 63 */ LocalizedNumberRangeFormatter locale(const icu::Locale &locale) const &; /** * Overload of locale() for use on an rvalue reference. * * @param locale * The locale to use when loading data for number formatting. * @return The fluent chain. * @see #locale * @stable ICU 63 */ LocalizedNumberRangeFormatter locale(const icu::Locale &locale) &&; /** * Default constructor: puts the formatter into a valid but undefined state. * * @stable ICU 63 */ UnlocalizedNumberRangeFormatter() = default; /** * Returns a copy of this UnlocalizedNumberRangeFormatter. * @stable ICU 63 */ UnlocalizedNumberRangeFormatter(const UnlocalizedNumberRangeFormatter &other); /** * Move constructor: * The source UnlocalizedNumberRangeFormatter will be left in a valid but undefined state. * @stable ICU 63 */ UnlocalizedNumberRangeFormatter(UnlocalizedNumberRangeFormatter&& src) U_NOEXCEPT; /** * Copy assignment operator. * @stable ICU 63 */ UnlocalizedNumberRangeFormatter& operator=(const UnlocalizedNumberRangeFormatter& other); /** * Move assignment operator: * The source UnlocalizedNumberRangeFormatter will be left in a valid but undefined state. * @stable ICU 63 */ UnlocalizedNumberRangeFormatter& operator=(UnlocalizedNumberRangeFormatter&& src) U_NOEXCEPT; private: explicit UnlocalizedNumberRangeFormatter( const NumberRangeFormatterSettings& other); explicit UnlocalizedNumberRangeFormatter( NumberRangeFormatterSettings&& src) U_NOEXCEPT; // To give the fluent setters access to this class's constructor: friend class NumberRangeFormatterSettings; // To give NumberRangeFormatter::with() access to this class's constructor: friend class NumberRangeFormatter; }; /** * A NumberRangeFormatter that has a locale associated with it; this means .formatRange() methods are available. * * Instances of this class are immutable and thread-safe. * * @see NumberFormatter * @stable ICU 63 */ class U_I18N_API LocalizedNumberRangeFormatter : public NumberRangeFormatterSettings, public UMemory { public: /** * Format the given Formattables to a string using the settings specified in the NumberRangeFormatter fluent setting * chain. * * @param first * The first number in the range, usually to the left in LTR locales. * @param second * The second number in the range, usually to the right in LTR locales. * @param status * Set if an error occurs while formatting. * @return A FormattedNumberRange object; call .toString() to get the string. * @stable ICU 63 */ FormattedNumberRange formatFormattableRange( const Formattable& first, const Formattable& second, UErrorCode& status) const; /** * Default constructor: puts the formatter into a valid but undefined state. * * @stable ICU 63 */ LocalizedNumberRangeFormatter() = default; /** * Returns a copy of this LocalizedNumberRangeFormatter. * @stable ICU 63 */ LocalizedNumberRangeFormatter(const LocalizedNumberRangeFormatter &other); /** * Move constructor: * The source LocalizedNumberRangeFormatter will be left in a valid but undefined state. * @stable ICU 63 */ LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) U_NOEXCEPT; /** * Copy assignment operator. * @stable ICU 63 */ LocalizedNumberRangeFormatter& operator=(const LocalizedNumberRangeFormatter& other); /** * Move assignment operator: * The source LocalizedNumberRangeFormatter will be left in a valid but undefined state. * @stable ICU 63 */ LocalizedNumberRangeFormatter& operator=(LocalizedNumberRangeFormatter&& src) U_NOEXCEPT; #ifndef U_HIDE_INTERNAL_API /** * @param results * The results object. This method will mutate it to save the results. * @param equalBeforeRounding * Whether the number was equal before copying it into a DecimalQuantity. * Used for determining the identity fallback behavior. * @param status * Set if an error occurs while formatting. * @internal */ void formatImpl(impl::UFormattedNumberRangeData& results, bool equalBeforeRounding, UErrorCode& status) const; #endif /* U_HIDE_INTERNAL_API */ /** * Destruct this LocalizedNumberRangeFormatter, cleaning up any memory it might own. * @stable ICU 63 */ ~LocalizedNumberRangeFormatter(); private: std::atomic fAtomicFormatter = {}; const impl::NumberRangeFormatterImpl* getFormatter(UErrorCode& stauts) const; explicit LocalizedNumberRangeFormatter( const NumberRangeFormatterSettings& other); explicit LocalizedNumberRangeFormatter( NumberRangeFormatterSettings&& src) U_NOEXCEPT; LocalizedNumberRangeFormatter(const impl::RangeMacroProps ¯os, const Locale &locale); LocalizedNumberRangeFormatter(impl::RangeMacroProps &¯os, const Locale &locale); void clear(); // To give the fluent setters access to this class's constructor: friend class NumberRangeFormatterSettings; friend class NumberRangeFormatterSettings; // To give UnlocalizedNumberRangeFormatter::locale() access to this class's constructor: friend class UnlocalizedNumberRangeFormatter; }; /** * The result of a number range 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 63 */ class U_I18N_API FormattedNumberRange : public UMemory, public FormattedValue { public: // Copybrief: this method is older than the parent method /** * @copybrief FormattedValue::toString() * * For more information, see FormattedValue::toString() * * @stable ICU 63 */ 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 63 */ 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; #ifndef U_HIDE_DRAFT_API /** * Export the first formatted number as a decimal number. This endpoint * is useful for obtaining the exact number being printed after scaling * and rounding have been applied by the number range formatting pipeline. * * The syntax of the unformatted number is a "numeric string" * as defined in the Decimal Arithmetic Specification, available at * http://speleotrove.com/decimal * * @return A decimal representation of the first formatted number. * @draft ICU 63 * @see NumberRangeFormatter * @see #getSecondDecimal */ UnicodeString getFirstDecimal(UErrorCode& status) const; /** * Export the second formatted number as a decimal number. This endpoint * is useful for obtaining the exact number being printed after scaling * and rounding have been applied by the number range formatting pipeline. * * The syntax of the unformatted number is a "numeric string" * as defined in the Decimal Arithmetic Specification, available at * http://speleotrove.com/decimal * * @return A decimal representation of the second formatted number. * @draft ICU 63 * @see NumberRangeFormatter * @see #getFirstDecimal */ UnicodeString getSecondDecimal(UErrorCode& status) const; #endif // U_HIDE_DRAFT_API /** * Returns whether the pair of numbers was successfully formatted as a range or whether an identity fallback was * used. For example, if the first and second number were the same either before or after rounding occurred, an * identity fallback was used. * * @return An indication the resulting identity situation in the formatted number range. * @stable ICU 63 * @see UNumberRangeIdentityFallback */ UNumberRangeIdentityResult getIdentityResult(UErrorCode& status) const; /** * Copying not supported; use move constructor instead. */ FormattedNumberRange(const FormattedNumberRange&) = delete; /** * Copying not supported; use move assignment instead. */ FormattedNumberRange& operator=(const FormattedNumberRange&) = delete; /** * Move constructor: * Leaves the source FormattedNumberRange in an undefined state. * @stable ICU 63 */ FormattedNumberRange(FormattedNumberRange&& src) U_NOEXCEPT; /** * Move assignment: * Leaves the source FormattedNumberRange in an undefined state. * @stable ICU 63 */ FormattedNumberRange& operator=(FormattedNumberRange&& src) U_NOEXCEPT; /** * Destruct an instance of FormattedNumberRange, cleaning up any memory it might own. * @stable ICU 63 */ ~FormattedNumberRange(); private: // Can't use LocalPointer because UFormattedNumberRangeData is forward-declared const impl::UFormattedNumberRangeData *fData; // Error code for the terminal methods UErrorCode fErrorCode; /** * Internal constructor from data type. Adopts the data pointer. */ explicit FormattedNumberRange(impl::UFormattedNumberRangeData *results) : fData(results), fErrorCode(U_ZERO_ERROR) {} explicit FormattedNumberRange(UErrorCode errorCode) : fData(nullptr), fErrorCode(errorCode) {} void getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, UErrorCode& status) const; // To give LocalizedNumberRangeFormatter format methods access to this class's constructor: friend class LocalizedNumberRangeFormatter; }; /** * See the main description in numberrangeformatter.h for documentation and examples. * * @stable ICU 63 */ class U_I18N_API NumberRangeFormatter final { public: /** * Call this method at the beginning of a NumberRangeFormatter fluent chain in which the locale is not currently * known at the call site. * * @return An {@link UnlocalizedNumberRangeFormatter}, to be used for chaining. * @stable ICU 63 */ static UnlocalizedNumberRangeFormatter with(); /** * Call this method at the beginning of a NumberRangeFormatter fluent chain in which the locale is known at the call * site. * * @param locale * The locale from which to load formats and symbols for number range formatting. * @return A {@link LocalizedNumberRangeFormatter}, to be used for chaining. * @stable ICU 63 */ static LocalizedNumberRangeFormatter withLocale(const Locale &locale); /** * Use factory methods instead of the constructor to create a NumberFormatter. */ NumberRangeFormatter() = delete; }; } // namespace number U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ #endif /* U_SHOW_CPLUSPLUS_API */ #endif // __NUMBERRANGEFORMATTER_H__