]> granicus.if.org Git - icu/commitdiff
ICU-11276 Adding C++ base implementation of NumberRangeFormatter, including unit...
authorShane Carr <shane@unicode.org>
Wed, 29 Aug 2018 08:22:21 +0000 (01:22 -0700)
committerShane Carr <shane@unicode.org>
Thu, 27 Sep 2018 21:27:39 +0000 (14:27 -0700)
14 files changed:
icu4c/source/i18n/Makefile.in
icu4c/source/i18n/i18n.vcxproj
icu4c/source/i18n/i18n.vcxproj.filters
icu4c/source/i18n/i18n_uwp.vcxproj
icu4c/source/i18n/numrange_fluent.cpp [new file with mode: 0644]
icu4c/source/i18n/numrange_impl.cpp [new file with mode: 0644]
icu4c/source/i18n/numrange_types.h
icu4c/source/i18n/unicode/numberrangeformatter.h
icu4c/source/test/intltest/Makefile.in
icu4c/source/test/intltest/intltest.vcxproj
icu4c/source/test/intltest/intltest.vcxproj.filters
icu4c/source/test/intltest/numbertest.h
icu4c/source/test/intltest/numbertest_range.cpp [new file with mode: 0644]
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberRangeFormatterTest.java

index 98b36b4c9177fd2b7651ef266eeb8e1dda2a044e..fb5eb146becf096c6040cb250285755d28f00af7 100644 (file)
@@ -111,9 +111,9 @@ double-conversion-fast-dtoa.o double-conversion-strtod.o \
 numparse_stringsegment.o numparse_parsednumber.o numparse_impl.o \
 numparse_symbols.o numparse_decimal.o numparse_scientific.o numparse_currency.o \
 numparse_affixes.o numparse_compositions.o numparse_validators.o \
+numrange_fluent.o numrange_impl.o \
 erarules.o
 
-
 ## Header files to install
 HEADERS = $(srcdir)/unicode/*.h
 
index 18f48c6584ea3203f860fff7b6b7b48b16ff5440..25b62d36cdbc59c5eb368241e8c1b9b250e6f944 100644 (file)
     <ClCompile Include="numparse_affixes.cpp" />
     <ClCompile Include="numparse_compositions.cpp" />
     <ClCompile Include="numparse_validators.cpp" />
+    <ClCompile Include="numrange_fluent.cpp" />
+    <ClCompile Include="numrange_impl.cpp" />
     <ClCompile Include="numfmt.cpp" />
     <ClCompile Include="numsys.cpp" />
     <ClCompile Include="olsontz.cpp" />
     <ClInclude Include="numparse_validators.h" />
     <ClInclude Include="numparse_types.h" />
     <ClInclude Include="numparse_utils.h" />
+    <ClInclude Include="numrange_types.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="i18n.rc" />
index db3470f83b20150c81cb39e9ed4c356ab3616bdb..ca431e607fc924b3c8b5f1a720105fd4db735678 100644 (file)
     <ClCompile Include="numparse_validators.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
+    <ClCompile Include="numrange_fluent.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
+    <ClCompile Include="numrange_impl.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
     <ClCompile Include="dayperiodrules.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
     <ClInclude Include="numparse_utils.h">
       <Filter>formatting</Filter>
     </ClInclude>
+    <ClInclude Include="numrange_types.h">
+      <Filter>formatting</Filter>
+    </ClInclude>
     <ClInclude Include="olsontz.h">
       <Filter>formatting</Filter>
     </ClInclude>
index e85c27766ab05b6175d95070334bb317ad8a44d3..3b0b18231ae32e0993f8af0124d3104e838afbbb 100644 (file)
     <ClCompile Include="numparse_affixes.cpp" />
     <ClCompile Include="numparse_compositions.cpp" />
     <ClCompile Include="numparse_validators.cpp" />
+    <ClCompile Include="numrange_fluent.cpp" />
+    <ClCompile Include="numrange_impl.cpp" />
     <ClCompile Include="numfmt.cpp" />
     <ClCompile Include="numsys.cpp" />
     <ClCompile Include="olsontz.cpp" />
     <ClInclude Include="numparse_validators.h" />
     <ClInclude Include="numparse_types.h" />
     <ClInclude Include="numparse_utils.h" />
+    <ClInclude Include="numrange_types.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="i18n.rc" />
diff --git a/icu4c/source/i18n/numrange_fluent.cpp b/icu4c/source/i18n/numrange_fluent.cpp
new file mode 100644 (file)
index 0000000..b6fc758
--- /dev/null
@@ -0,0 +1,381 @@
+// © 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 && !UPRV_INCOMPLETE_CPP11_SUPPORT
+
+// Allow implicit conversion from char16_t* to UnicodeString for this file:
+// Helpful in toString methods and elsewhere.
+#define UNISTR_FROM_STRING_EXPLICIT
+
+#include "numrange_types.h"
+#include "util.h"
+#include "number_utypes.h"
+
+using namespace icu;
+using namespace icu::number;
+using namespace icu::number::impl;
+
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) const& {
+    Derived copy(*this);
+    copy.fMacros.formatter1 = formatter;
+    copy.fMacros.formatter2 = formatter;
+    return copy;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(const UnlocalizedNumberFormatter& formatter) && {
+    Derived move(std::move(*this));
+    move.fMacros.formatter1 = formatter;
+    move.fMacros.formatter2 = formatter;
+    return move;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) const& {
+    Derived copy(*this);
+    copy.fMacros.formatter1 = formatter;
+    copy.fMacros.formatter2 = std::move(formatter);
+    return copy;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterBoth(UnlocalizedNumberFormatter&& formatter) && {
+    Derived move(std::move(*this));
+    move.fMacros.formatter1 = formatter;
+    move.fMacros.formatter2 = std::move(formatter);
+    return move;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) const& {
+    Derived copy(*this);
+    copy.fMacros.formatter1 = formatter;
+    return copy;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(const UnlocalizedNumberFormatter& formatter) && {
+    Derived move(std::move(*this));
+    move.fMacros.formatter1 = formatter;
+    return move;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) const& {
+    Derived copy(*this);
+    copy.fMacros.formatter1 = std::move(formatter);
+    return copy;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterFirst(UnlocalizedNumberFormatter&& formatter) && {
+    Derived move(std::move(*this));
+    move.fMacros.formatter1 = std::move(formatter);
+    return move;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) const& {
+    Derived copy(*this);
+    copy.fMacros.formatter2 = formatter;
+    return copy;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(const UnlocalizedNumberFormatter& formatter) && {
+    Derived move(std::move(*this));
+    move.fMacros.formatter2 = formatter;
+    return move;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) const& {
+    Derived copy(*this);
+    copy.fMacros.formatter2 = std::move(formatter);
+    return copy;
+}
+
+template<typename Derived>
+Derived NumberRangeFormatterSettings<Derived>::numberFormatterSecond(UnlocalizedNumberFormatter&& formatter) && {
+    Derived move(std::move(*this));
+    move.fMacros.formatter2 = std::move(formatter);
+    return move;
+}
+
+// Declare all classes that implement NumberRangeFormatterSettings
+// See https://stackoverflow.com/a/495056/1407170
+template
+class icu::number::NumberRangeFormatterSettings<icu::number::UnlocalizedNumberRangeFormatter>;
+template
+class icu::number::NumberRangeFormatterSettings<icu::number::LocalizedNumberRangeFormatter>;
+
+
+UnlocalizedNumberRangeFormatter NumberRangeFormatter::with() {
+    UnlocalizedNumberRangeFormatter result;
+    return result;
+}
+
+LocalizedNumberRangeFormatter NumberRangeFormatter::withLocale(const Locale& locale) {
+    return with().locale(locale);
+}
+
+
+template<typename T> using NFS = NumberRangeFormatterSettings<T>;
+using LNF = LocalizedNumberRangeFormatter;
+using UNF = UnlocalizedNumberRangeFormatter;
+
+UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const UNF& other)
+        : UNF(static_cast<const NFS<UNF>&>(other)) {}
+
+UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(const NFS<UNF>& other)
+        : NFS<UNF>(other) {
+    // No additional fields to assign
+}
+
+UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(UNF&& src) U_NOEXCEPT
+        : UNF(static_cast<NFS<UNF>&&>(src)) {}
+
+UnlocalizedNumberRangeFormatter::UnlocalizedNumberRangeFormatter(NFS<UNF>&& src) U_NOEXCEPT
+        : NFS<UNF>(std::move(src)) {
+    // No additional fields to assign
+}
+
+UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(const UNF& other) {
+    NFS<UNF>::operator=(static_cast<const NFS<UNF>&>(other));
+    // No additional fields to assign
+    return *this;
+}
+
+UnlocalizedNumberRangeFormatter& UnlocalizedNumberRangeFormatter::operator=(UNF&& src) U_NOEXCEPT {
+    NFS<UNF>::operator=(static_cast<NFS<UNF>&&>(src));
+    // No additional fields to assign
+    return *this;
+}
+
+LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const LNF& other)
+        : LNF(static_cast<const NFS<LNF>&>(other)) {}
+
+LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const NFS<LNF>& other)
+        : NFS<LNF>(other) {
+    // No additional fields to assign
+}
+
+LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) U_NOEXCEPT
+        : LNF(static_cast<NFS<LNF>&&>(src)) {}
+
+LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(NFS<LNF>&& src) U_NOEXCEPT
+        : NFS<LNF>(std::move(src)) {
+    // No additional fields to assign
+}
+
+LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(const LNF& other) {
+    NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
+    // No additional fields to assign
+    return *this;
+}
+
+LocalizedNumberRangeFormatter& LocalizedNumberRangeFormatter::operator=(LNF&& src) U_NOEXCEPT {
+    NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src));
+    // No additional fields to assign
+    return *this;
+}
+
+
+LocalizedNumberRangeFormatter::~LocalizedNumberRangeFormatter() = default;
+
+LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(const RangeMacroProps& macros, const Locale& locale) {
+    fMacros = macros;
+    fMacros.locale = locale;
+}
+
+LocalizedNumberRangeFormatter::LocalizedNumberRangeFormatter(RangeMacroProps&& macros, const Locale& locale) {
+    fMacros = std::move(macros);
+    fMacros.locale = locale;
+}
+
+LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale) const& {
+    return LocalizedNumberRangeFormatter(fMacros, locale);
+}
+
+LocalizedNumberRangeFormatter UnlocalizedNumberRangeFormatter::locale(const Locale& locale)&& {
+    return LocalizedNumberRangeFormatter(std::move(fMacros), locale);
+}
+
+
+FormattedNumberRange LocalizedNumberRangeFormatter::formatFormattableRange(
+        const Formattable& first, const Formattable& second, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return FormattedNumberRange(U_ILLEGAL_ARGUMENT_ERROR);
+    }
+
+    auto results = new UFormattedNumberRangeData();
+    if (results == nullptr) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return FormattedNumberRange(status);
+    }
+
+    first.populateDecimalQuantity(results->quantity1, status);
+    if (U_FAILURE(status)) {
+        return FormattedNumberRange(status);
+    }
+
+    second.populateDecimalQuantity(results->quantity2, status);
+    if (U_FAILURE(status)) {
+        return FormattedNumberRange(status);
+    }
+
+    formatImpl(results, status);
+
+    // Do not save the results object if we encountered a failure.
+    if (U_SUCCESS(status)) {
+        return FormattedNumberRange(results);
+    } else {
+        delete results;
+        return FormattedNumberRange(status);
+    }
+}
+
+void LocalizedNumberRangeFormatter::formatImpl(
+        UFormattedNumberRangeData* results, UErrorCode& status) const {
+    // TODO: This is a placeholder implementation.
+
+    UFormattedNumberData r1;
+    r1.quantity = results->quantity1;
+    fMacros.formatter1.locale(fMacros.locale).formatImpl(&r1, status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+    results->quantity1 = r1.quantity;
+
+    UFormattedNumberData r2;
+    r2.quantity = results->quantity2;
+    fMacros.formatter2.locale(fMacros.locale).formatImpl(&r2, status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+    results->quantity2 = r2.quantity;
+
+    results->string.append(r1.string, status);
+    results->string.append(u" --- ", UNUM_FIELD_COUNT, status);
+    results->string.append(r2.string, status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    results->identityType = UNUM_IDENTITY_TYPE_NOT_EQUAL;
+}
+
+
+FormattedNumberRange::FormattedNumberRange(FormattedNumberRange&& src) U_NOEXCEPT
+        : fResults(src.fResults), fErrorCode(src.fErrorCode) {
+    // Disown src.fResults to prevent double-deletion
+    src.fResults = nullptr;
+    src.fErrorCode = U_INVALID_STATE_ERROR;
+}
+
+FormattedNumberRange& FormattedNumberRange::operator=(FormattedNumberRange&& src) U_NOEXCEPT {
+    delete fResults;
+    fResults = src.fResults;
+    fErrorCode = src.fErrorCode;
+    // Disown src.fResults to prevent double-deletion
+    src.fResults = nullptr;
+    src.fErrorCode = U_INVALID_STATE_ERROR;
+    return *this;
+}
+
+UnicodeString FormattedNumberRange::toString(UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return ICU_Utility::makeBogusString();
+    }
+    if (fResults == nullptr) {
+        status = fErrorCode;
+        return ICU_Utility::makeBogusString();
+    }
+    return fResults->string.toUnicodeString();
+}
+
+Appendable& FormattedNumberRange::appendTo(Appendable& appendable, UErrorCode& status) {
+    if (U_FAILURE(status)) {
+        return appendable;
+    }
+    if (fResults == nullptr) {
+        status = fErrorCode;
+        return appendable;
+    }
+    appendable.appendString(fResults->string.chars(), fResults->string.length());
+    return appendable;
+}
+
+UBool FormattedNumberRange::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return FALSE;
+    }
+    if (fResults == nullptr) {
+        status = fErrorCode;
+        return FALSE;
+    }
+    // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
+    return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
+}
+
+void FormattedNumberRange::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
+    FieldPositionIteratorHandler fpih(&iterator, status);
+    getAllFieldPositionsImpl(fpih, status);
+}
+
+void FormattedNumberRange::getAllFieldPositionsImpl(
+        FieldPositionIteratorHandler& fpih, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (fResults == nullptr) {
+        status = fErrorCode;
+        return;
+    }
+    fResults->string.getAllFieldPositions(fpih, status);
+}
+
+UnicodeString FormattedNumberRange::getFirstDecimal(UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return ICU_Utility::makeBogusString();
+    }
+    if (fResults == nullptr) {
+        status = fErrorCode;
+        return ICU_Utility::makeBogusString();
+    }
+    return fResults->quantity1.toScientificString();
+}
+
+UnicodeString FormattedNumberRange::getSecondDecimal(UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return ICU_Utility::makeBogusString();
+    }
+    if (fResults == nullptr) {
+        status = fErrorCode;
+        return ICU_Utility::makeBogusString();
+    }
+    return fResults->quantity2.toScientificString();
+}
+
+UNumberRangeIdentityType FormattedNumberRange::getIdentityType(UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return UNUM_IDENTITY_TYPE_NOT_EQUAL;
+    }
+    if (fResults == nullptr) {
+        status = fErrorCode;
+        return UNUM_IDENTITY_TYPE_NOT_EQUAL;
+    }
+    return fResults->identityType;
+}
+
+FormattedNumberRange::~FormattedNumberRange() {
+    delete fResults;
+}
+
+
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/numrange_impl.cpp b/icu4c/source/i18n/numrange_impl.cpp
new file mode 100644 (file)
index 0000000..1b3a685
--- /dev/null
@@ -0,0 +1,23 @@
+// © 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 && !UPRV_INCOMPLETE_CPP11_SUPPORT
+
+// Allow implicit conversion from char16_t* to UnicodeString for this file:
+// Helpful in toString methods and elsewhere.
+#define UNISTR_FROM_STRING_EXPLICIT
+
+#include "numrange_types.h"
+
+using namespace icu;
+using namespace icu::number;
+using namespace icu::number::impl;
+
+
+
+
+
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
index 77b0ea619c99057fda81923746a117168f6796c9..c3fef3789af051a2e26d4dea44fdfc2bc02c0e0c 100644 (file)
@@ -36,7 +36,7 @@ struct UFormattedNumberRangeData : public UMemory {
     DecimalQuantity quantity1;
     DecimalQuantity quantity2;
     NumberStringBuilder string;
-    UNumberRangeIdentityType identityType;
+    UNumberRangeIdentityType identityType = UNUM_IDENTITY_TYPE_EQUAL_BEFORE_ROUNDING;
 
     // No C conversion methods (no C API yet)
 };
index d4e1e6be4ecf8ba99186afeefb2029a154131998..fe28ccf393a8d416dffa27bae3c48e54c7c00ae3 100644 (file)
@@ -162,6 +162,22 @@ U_NAMESPACE_BEGIN
 
 namespace number {  // icu::number
 
+// Forward declarations:
+class UnlocalizedNumberRangeFormatter;
+class LocalizedNumberRangeFormatter;
+class FormattedNumberRange;
+
+namespace impl {
+
+// Forward declarations:
+struct RangeMacroProps;
+class DecimalQuantity;
+struct UFormattedNumberRangeData;
+
+} // namespace impl
+
+// 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
@@ -177,7 +193,7 @@ struct U_I18N_API RangeMacroProps : public UMemory {
     UNumberRangeCollapse collapse = UNUM_RANGE_COLLAPSE_AUTO;
 
     /** @internal */
-    UNumberIdentityFallback identityFallback = UNUM_IDENTITY_APPROXIMATELY;
+    UNumberIdentityFallback identityFallback = UNUM_IDENTITY_FALLBACK_APPROXIMATELY;
 
     /** @internal */
     Locale locale;
@@ -532,7 +548,7 @@ class U_I18N_API LocalizedNumberRangeFormatter
      * @draft ICU 63
      */
     FormattedNumberRange formatFormattableRange(
-        const Formattable& first, const Formattable& second) const;
+        const Formattable& first, const Formattable& second, UErrorCode& status) const;
 
     /**
      * Default constructor: puts the formatter into a valid but undefined state.
@@ -586,10 +602,6 @@ class U_I18N_API LocalizedNumberRangeFormatter
     ~LocalizedNumberRangeFormatter();
 
   private:
-    // Note: fCompiled can't be a LocalPointer because impl::NumberFormatterImpl is defined in an internal
-    // header, and LocalPointer needs the full class definition in order to delete the instance.
-    const impl::NumberFormatterImpl* fCompiled {nullptr};
-    char fUnsafeCallCount[8] {};  // internally cast to u_atomic_int32_t
 
     explicit LocalizedNumberRangeFormatter(
         const NumberRangeFormatterSettings<LocalizedNumberRangeFormatter>& other);
@@ -701,7 +713,7 @@ class U_I18N_API FormattedNumberRange : public UMemory {
      * @see NumberRangeFormatter
      * @see #getSecondDecimal
      */
-    UnicodeString getFirstDecimal() const;
+    UnicodeString getFirstDecimal(UErrorCode& status) const;
 
     /**
      * Export the second formatted number as a decimal number. This endpoint
@@ -718,7 +730,7 @@ class U_I18N_API FormattedNumberRange : public UMemory {
      * @see NumberRangeFormatter
      * @see #getFirstDecimal
      */
-    UnicodeString getSecondDecimal() const;
+    UnicodeString getSecondDecimal(UErrorCode& status) const;
 
     /**
      * Returns whether the pair of numbers was successfully formatted as a range or whether an identity fallback was
@@ -730,7 +742,7 @@ class U_I18N_API FormattedNumberRange : public UMemory {
      * @provisional This API might change or be removed in a future release.
      * @see UNumberRangeIdentityFallback
      */
-    UNumberRangeIdentityType getIdentityType() const;
+    UNumberRangeIdentityType getIdentityType(UErrorCode& status) const;
 
     /**
      * Copying not supported; use move constructor instead.
@@ -779,6 +791,8 @@ class U_I18N_API FormattedNumberRange : public UMemory {
     explicit FormattedNumberRange(UErrorCode errorCode)
         : fResults(nullptr), fErrorCode(errorCode) {};
 
+    void getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, UErrorCode& status) const;
+
     // To give LocalizedNumberRangeFormatter format methods access to this class's constructor:
     friend class LocalizedNumberRangeFormatter;
 };
@@ -797,7 +811,7 @@ class U_I18N_API NumberRangeFormatter final {
      * @return An {@link UnlocalizedNumberRangeFormatter}, to be used for chaining.
      * @draft ICU 63
      */
-    static UnlocalizedNumberFormatter with();
+    static UnlocalizedNumberRangeFormatter with();
 
     /**
      * Call this method at the beginning of a NumberRangeFormatter fluent chain in which the locale is known at the call
@@ -808,7 +822,7 @@ class U_I18N_API NumberRangeFormatter final {
      * @return A {@link LocalizedNumberRangeFormatter}, to be used for chaining.
      * @draft ICU 63
      */
-    static LocalizedNumberFormatter withLocale(const Locale &locale);
+    static LocalizedNumberRangeFormatter withLocale(const Locale &locale);
 
     /**
      * Use factory methods instead of the constructor to create a NumberFormatter.
index 4d3e17435ea5de6f5e8672d1678c89693430e6c5..ad47ad0a14b219b7b7b9dee077f747715ed64ba1 100644 (file)
@@ -66,7 +66,7 @@ numbertest_affixutils.o numbertest_api.o numbertest_decimalquantity.o \
 numbertest_modifiers.o numbertest_patternmodifier.o numbertest_patternstring.o \
 numbertest_stringbuilder.o numbertest_stringsegment.o \
 numbertest_parse.o numbertest_doubleconversion.o numbertest_skeletons.o \
-static_unisets_test.o numfmtdatadriventest.o erarulestest.o
+static_unisets_test.o numfmtdatadriventest.o numbertest_range.o erarulestest.o
 
 DEPS = $(OBJECTS:.o=.d)
 
index 60d34a0c95710719dec5698e0846bb5a20235b65..50e0e2e89c2b28f0d9a9d66119fe1629015bec4e 100644 (file)
     <ClCompile Include="numbertest_parse.cpp" />
     <ClCompile Include="numbertest_doubleconversion.cpp" />
     <ClCompile Include="numbertest_skeletons.cpp" />
+    <ClCompile Include="numbertest_range.cpp" />
     <ClCompile Include="numfmtst.cpp" />
     <ClCompile Include="numfmtdatadriventest.cpp" />
     <ClCompile Include="numrgts.cpp" />
index fc2ecb53050d4efb58d46ea1f277391266f5a06c..f1b740dbe99801df5d346004ba897e788cc7bdfa 100644 (file)
     <ClCompile Include="numbertest_skeletons.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
+    <ClCompile Include="numbertest_range.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
     <ClCompile Include="numfmtst.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
index 8984fdc366980cf2338e55b1640ae4a4c1e5f42a..2618b97ddf3f3396661038e26db9e5b7a6b5ba36 100644 (file)
@@ -11,6 +11,8 @@
 #include "number_affixutils.h"
 #include "numparse_stringsegment.h"
 #include "unicode/locid.h"
+#include "unicode/numberformatter.h"
+#include "unicode/numberrangeformatter.h"
 
 using namespace icu::number;
 using namespace icu::number::impl;
@@ -244,6 +246,37 @@ class NumberSkeletonTest : public IntlTest {
     void expectedErrorSkeleton(const char16_t** cases, int32_t casesLen);
 };
 
+class NumberRangeFormatterTest : public IntlTest {
+  public:
+    void testSanity();
+    void testBasic();
+
+    void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
+
+  private:
+    void assertFormatRange(
+      const char16_t* message,
+      const UnlocalizedNumberRangeFormatter& f,
+      Locale locale,
+      const char16_t* expected_10_50,
+      const char16_t* expected_49_51,
+      const char16_t* expected_50_50,
+      const char16_t* expected_00_30,
+      const char16_t* expected_00_00,
+      const char16_t* expected_30_3K,
+      const char16_t* expected_30K_50K,
+      const char16_t* expected_49K_51K,
+      const char16_t* expected_50K_50K,
+      const char16_t* expected_50K_50M);
+    
+    void assertFormattedRangeEquals(
+      const char16_t* message,
+      const LocalizedNumberRangeFormatter& l,
+      double first,
+      double second,
+      const char16_t* expected);
+};
+
 
 // NOTE: This macro is identical to the one in itformat.cpp
 #define TESTCLASS(id, TestClass)          \
@@ -276,6 +309,7 @@ class NumberTest : public IntlTest {
         TESTCLASS(8, StringSegmentTest);
         TESTCLASS(9, NumberParserTest);
         TESTCLASS(10, NumberSkeletonTest);
+        TESTCLASS(11, NumberRangeFormatterTest);
         default: name = ""; break; // needed to end loop
         }
     }
diff --git a/icu4c/source/test/intltest/numbertest_range.cpp b/icu4c/source/test/intltest/numbertest_range.cpp
new file mode 100644 (file)
index 0000000..3370edc
--- /dev/null
@@ -0,0 +1,93 @@
+// © 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 "numbertest.h"
+#include "unicode/numberrangeformatter.h"
+
+#include <cmath>
+#include <numparse_affixes.h>
+
+using icu::unisets::get;
+
+void NumberRangeFormatterTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
+    if (exec) {
+        logln("TestSuite NumberRangeFormatterTest: ");
+    }
+    TESTCASE_AUTO_BEGIN;
+        TESTCASE_AUTO(testSanity);
+        TESTCASE_AUTO(testBasic);
+    TESTCASE_AUTO_END;
+}
+
+void NumberRangeFormatterTest::testSanity() {
+    IcuTestErrorCode status(*this, "testSanity");
+    LocalizedNumberRangeFormatter lnrf1 = NumberRangeFormatter::withLocale("en-us");
+    LocalizedNumberRangeFormatter lnrf2 = NumberRangeFormatter::with().locale("en-us");
+    assertEquals("Formatters should have same behavior 1",
+        lnrf1.formatFormattableRange(4, 6, status).toString(status),
+        lnrf2.formatFormattableRange(4, 6, status).toString(status));
+}
+
+void NumberRangeFormatterTest::testBasic() {
+    assertFormatRange(
+        u"Basic",
+        NumberRangeFormatter::with(),
+        Locale("en-us"),
+        u"1 --- 5",
+        u"5 --- 5",
+        u"5 --- 5",
+        u"0 --- 3",
+        u"0 --- 0",
+        u"3 --- 3,000",
+        u"3,000 --- 5,000",
+        u"4,999 --- 5,001",
+        u"5,000 --- 5,000",
+        u"5,000 --- 5,000,000");
+}
+
+void  NumberRangeFormatterTest::assertFormatRange(
+      const char16_t* message,
+      const UnlocalizedNumberRangeFormatter& f,
+      Locale locale,
+      const char16_t* expected_10_50,
+      const char16_t* expected_49_51,
+      const char16_t* expected_50_50,
+      const char16_t* expected_00_30,
+      const char16_t* expected_00_00,
+      const char16_t* expected_30_3K,
+      const char16_t* expected_30K_50K,
+      const char16_t* expected_49K_51K,
+      const char16_t* expected_50K_50K,
+      const char16_t* expected_50K_50M) {
+    LocalizedNumberRangeFormatter l = f.locale(locale);
+    assertFormattedRangeEquals(message, l, 1, 5, expected_10_50);
+    assertFormattedRangeEquals(message, l, 4.9999999, 5.0000001, expected_49_51);
+    assertFormattedRangeEquals(message, l, 5, 5, expected_50_50);
+    assertFormattedRangeEquals(message, l, 0, 3, expected_00_30);
+    assertFormattedRangeEquals(message, l, 0, 0, expected_00_00);
+    assertFormattedRangeEquals(message, l, 3, 3000, expected_30_3K);
+    assertFormattedRangeEquals(message, l, 3000, 5000, expected_30K_50K);
+    assertFormattedRangeEquals(message, l, 4999, 5001, expected_49K_51K);
+    assertFormattedRangeEquals(message, l, 5000, 5000, expected_50K_50K);
+    assertFormattedRangeEquals(message, l, 5e3, 5e6, expected_50K_50M);
+}
+
+void NumberRangeFormatterTest::assertFormattedRangeEquals(
+      const char16_t* message,
+      const LocalizedNumberRangeFormatter& l,
+      double first,
+      double second,
+      const char16_t* expected) {
+    IcuTestErrorCode status(*this, "assertFormattedRangeEquals");
+    UnicodeString fullMessage = UnicodeString(message) + u": " + DoubleToUnicodeString(first) + u", " + DoubleToUnicodeString(second);
+    status.setScope(fullMessage);
+    UnicodeString actual = l.formatFormattableRange(first, second, status).toString(status);
+    assertEquals(fullMessage, expected, actual);
+}
+
+
+#endif
index 1e150d3fe4c76bec72c087e559f11d98a569f405..82510a7a531ac4ee9927bc93661f33c02aaa5f0d 100644 (file)
@@ -153,8 +153,8 @@ public class NumberRangeFormatterTest {
 
     private static void assertFormattedRangeEquals(String message, LocalizedNumberRangeFormatter l, Number first,
             Number second, String expected) {
-        String actual1 = l.formatRange(first, second).toString();
-        assertEquals(message + ": " + first + ", " + second, expected, actual1);
+        String actual = l.formatRange(first, second).toString();
+        assertEquals(message + ": " + first + ", " + second, expected, actual);
     }
 
 }