]> granicus.if.org Git - icu/commitdiff
ICU-20409 Additional refactoring of FormattedValue implementations.
authorShane Carr <shane@unicode.org>
Fri, 15 Feb 2019 06:42:09 +0000 (22:42 -0800)
committerShane F. Carr <shane@unicode.org>
Sat, 16 Feb 2019 00:50:22 +0000 (16:50 -0800)
- Migrates Number[Range] formatter to helper macros.
- Adds additional macros.
- Syncs docstrings between subclasses.

23 files changed:
icu4c/source/i18n/formattedval_impl.h
icu4c/source/i18n/number_asformat.cpp
icu4c/source/i18n/number_capi.cpp
icu4c/source/i18n/number_fluent.cpp
icu4c/source/i18n/number_output.cpp
icu4c/source/i18n/number_utypes.h
icu4c/source/i18n/numrange_fluent.cpp
icu4c/source/i18n/numrange_impl.cpp
icu4c/source/i18n/numrange_impl.h
icu4c/source/i18n/plurfmt.cpp
icu4c/source/i18n/quantityformatter.cpp
icu4c/source/i18n/smpdtfmt.cpp
icu4c/source/i18n/unicode/dtitvfmt.h
icu4c/source/i18n/unicode/listformatter.h
icu4c/source/i18n/unicode/numberformatter.h
icu4c/source/i18n/unicode/numberrangeformatter.h
icu4c/source/i18n/unicode/reldatefmt.h
icu4c/source/i18n/unicode/unumberformatter.h
icu4c/source/test/cintltst/unumberformattertst.c
icu4c/source/test/depstest/dependencies.txt
icu4j/main/classes/core/src/com/ibm/icu/impl/Utility.java
icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumber.java
icu4j/main/classes/core/src/com/ibm/icu/text/RelativeDateTimeFormatter.java

index 5289f285c3f5c3ba4c365d14ffede52a51c65a6d..ee15ed9dfd0f7e583fe37d09dd5dd822887b072d 100644 (file)
@@ -130,6 +130,10 @@ public:
         return fString;
     }
 
+    inline const number::impl::NumberStringBuilder& getStringRef() const {
+        return fString;
+    }
+
 private:
     number::impl::NumberStringBuilder fString;
     number::impl::Field fNumericField;
@@ -146,12 +150,22 @@ struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper {
 };
 
 
+/** Boilerplate to check for valid status before dereferencing the fData pointer. */
+#define UPRV_FORMATTED_VALUE_METHOD_GUARD(returnExpression) \
+    if (U_FAILURE(status)) { \
+        return returnExpression; \
+    } \
+    if (fData == nullptr) { \
+        status = fErrorCode; \
+        return returnExpression; \
+    } \
+
+
 /** Implementation of the methods from U_FORMATTED_VALUE_SUBCLASS_AUTO. */
 #define UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(Name) \
-    Name::Name(Name&& src) U_NOEXCEPT \
-        fData = src.fData; \
+    Name::Name(Name&& src) U_NOEXCEPT \
+            : fData(src.fData), fErrorCode(src.fErrorCode) { \
         src.fData = nullptr; \
-        fErrorCode = src.fErrorCode; \
         src.fErrorCode = U_INVALID_STATE_ERROR; \
     } \
     Name::~Name() { \
@@ -167,44 +181,48 @@ struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper {
         return *this; \
     } \
     UnicodeString Name::toString(UErrorCode& status) const { \
-        if (U_FAILURE(status)) { \
-            return ICU_Utility::makeBogusString(); \
-        } \
-        if (fData == nullptr) { \
-            status = fErrorCode; \
-            return ICU_Utility::makeBogusString(); \
-        } \
+        UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \
         return fData->toString(status); \
     } \
     UnicodeString Name::toTempString(UErrorCode& status) const { \
-        if (U_FAILURE(status)) { \
-            return ICU_Utility::makeBogusString(); \
-        } \
-        if (fData == nullptr) { \
-            status = fErrorCode; \
-            return ICU_Utility::makeBogusString(); \
-        } \
+        UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \
         return fData->toTempString(status); \
     } \
     Appendable& Name::appendTo(Appendable& appendable, UErrorCode& status) const { \
-        if (U_FAILURE(status)) { \
-            return appendable; \
-        } \
-        if (fData == nullptr) { \
-            status = fErrorCode; \
-            return appendable; \
-        } \
+        UPRV_FORMATTED_VALUE_METHOD_GUARD(appendable) \
         return fData->appendTo(appendable, status); \
     } \
     UBool Name::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { \
-        if (U_FAILURE(status)) { \
-            return FALSE; \
+        UPRV_FORMATTED_VALUE_METHOD_GUARD(FALSE) \
+        return fData->nextPosition(cfpos, status); \
+    }
+
+
+/** Like UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL but without impl type declarations. */
+#define UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) \
+    U_CAPI CType* U_EXPORT2 \
+    Prefix ## _openResult (UErrorCode* ec) { \
+        if (U_FAILURE(*ec)) { \
+            return nullptr; \
         } \
-        if (fData == nullptr) { \
-            status = fErrorCode; \
-            return FALSE; \
+        ImplType* impl = new ImplType(); \
+        if (impl == nullptr) { \
+            *ec = U_MEMORY_ALLOCATION_ERROR; \
+            return nullptr; \
         } \
-        return fData->nextPosition(cfpos, status); \
+        return static_cast<HelperType*>(impl)->exportForC(); \
+    } \
+    U_DRAFT const UFormattedValue* U_EXPORT2 \
+    Prefix ## _resultAsValue (const CType* uresult, UErrorCode* ec) { \
+        const ImplType* result = HelperType::validate(uresult, *ec); \
+        if (U_FAILURE(*ec)) { return nullptr; } \
+        return static_cast<const UFormattedValueApiHelper*>(result)->exportConstForC(); \
+    } \
+    U_CAPI void U_EXPORT2 \
+    Prefix ## _closeResult (CType* uresult) { \
+        UErrorCode localStatus = U_ZERO_ERROR; \
+        const ImplType* impl = HelperType::validate(uresult, localStatus); \
+        delete impl; \
     }
 
 
@@ -232,30 +250,7 @@ struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper {
     } \
     ImplType::~ImplType() {} \
     U_NAMESPACE_END \
-    U_CAPI CType* U_EXPORT2 \
-    Prefix ## _openResult (UErrorCode* ec) { \
-        if (U_FAILURE(*ec)) { \
-            return nullptr; \
-        } \
-        ImplType* impl = new ImplType(); \
-        if (impl == nullptr) { \
-            *ec = U_MEMORY_ALLOCATION_ERROR; \
-            return nullptr; \
-        } \
-        return static_cast<HelperType*>(impl)->exportForC(); \
-    } \
-    U_DRAFT const UFormattedValue* U_EXPORT2 \
-    Prefix ## _resultAsValue (const CType* uresult, UErrorCode* ec) { \
-        const ImplType* result = HelperType::validate(uresult, *ec); \
-        if (U_FAILURE(*ec)) { return nullptr; } \
-        return static_cast<const UFormattedValueApiHelper*>(result)->exportConstForC(); \
-    } \
-    U_CAPI void U_EXPORT2 \
-    Prefix ## _closeResult (CType* uresult) { \
-        UErrorCode localStatus = U_ZERO_ERROR; \
-        const ImplType* impl = HelperType::validate(uresult, localStatus); \
-        delete impl; \
-    }
+    UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix)
 
 
 U_NAMESPACE_END
index c6bb538932cec84d390255b548435cd4c5cb3707..9d3ea69f578e1eb8757f08b27d76cad9d676d2e0 100644 (file)
@@ -62,12 +62,12 @@ UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj,
     // always return first occurrence:
     pos.setBeginIndex(0);
     pos.setEndIndex(0);
-    bool found = data.string.nextFieldPosition(pos, status);
+    bool found = data.getStringRef().nextFieldPosition(pos, status);
     if (found && appendTo.length() != 0) {
         pos.setBeginIndex(pos.getBeginIndex() + appendTo.length());
         pos.setEndIndex(pos.getEndIndex() + appendTo.length());
     }
-    appendTo.append(data.string.toTempUnicodeString());
+    appendTo.append(data.getStringRef().toTempUnicodeString());
     return appendTo;
 }
 
@@ -84,10 +84,10 @@ UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj,
     if (U_FAILURE(status)) {
         return appendTo;
     }
-    appendTo.append(data.string.toTempUnicodeString());
+    appendTo.append(data.getStringRef().toTempUnicodeString());
     if (posIter != nullptr) {
         FieldPositionIteratorHandler fpih(posIter, status);
-        data.string.getAllFieldPositions(fpih, status);
+        data.getStringRef().getAllFieldPositions(fpih, status);
     }
     return appendTo;
 }
index e67dd90fce745d206d30ce0a4b17378d161cdfa1..f802e211049eb17a3c93c52b282c77f2bf4b54bd 100644 (file)
@@ -54,7 +54,7 @@ UFormattedNumberImpl::UFormattedNumberImpl()
 
 UFormattedNumberImpl::~UFormattedNumberImpl() {
     // Disown the data from fImpl so it doesn't get deleted twice
-    fImpl.fResults = nullptr;
+    fImpl.fData = nullptr;
 }
 
 }
@@ -62,6 +62,13 @@ UFormattedNumberImpl::~UFormattedNumberImpl() {
 U_NAMESPACE_END
 
 
+UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(
+    UFormattedNumber,
+    UFormattedNumberImpl,
+    UFormattedNumberApiHelper,
+    unumf)
+
+
 const DecimalQuantity* icu::number::impl::validateUFormattedNumberToDecimalQuantity(
         const UFormattedNumber* uresult, UErrorCode& status) {
     auto* result = UFormattedNumberApiHelper::validate(uresult, status);
@@ -101,16 +108,6 @@ unumf_openForSkeletonAndLocaleWithError(const UChar* skeleton, int32_t skeletonL
     return impl->exportForC();
 }
 
-U_CAPI UFormattedNumber* U_EXPORT2
-unumf_openResult(UErrorCode* ec) {
-    auto* impl = new UFormattedNumberImpl();
-    if (impl == nullptr) {
-        *ec = U_MEMORY_ALLOCATION_ERROR;
-        return nullptr;
-    }
-    return static_cast<UFormattedNumberApiHelper*>(impl)->exportForC();
-}
-
 U_CAPI void U_EXPORT2
 unumf_formatInt(const UNumberFormatter* uformatter, int64_t value, UFormattedNumber* uresult,
                 UErrorCode* ec) {
@@ -118,7 +115,7 @@ unumf_formatInt(const UNumberFormatter* uformatter, int64_t value, UFormattedNum
     auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
     if (U_FAILURE(*ec)) { return; }
 
-    result->fData.string.clear();
+    result->fData.getStringRef().clear();
     result->fData.quantity.setToLong(value);
     formatter->fFormatter.formatImpl(&result->fData, *ec);
 }
@@ -130,7 +127,7 @@ unumf_formatDouble(const UNumberFormatter* uformatter, double value, UFormattedN
     auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
     if (U_FAILURE(*ec)) { return; }
 
-    result->fData.string.clear();
+    result->fData.getStringRef().clear();
     result->fData.quantity.setToDouble(value);
     formatter->fFormatter.formatImpl(&result->fData, *ec);
 }
@@ -142,20 +139,12 @@ unumf_formatDecimal(const UNumberFormatter* uformatter, const char* value, int32
     auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
     if (U_FAILURE(*ec)) { return; }
 
-    result->fData.string.clear();
+    result->fData.getStringRef().clear();
     result->fData.quantity.setToDecNumber({value, valueLen}, *ec);
     if (U_FAILURE(*ec)) { return; }
     formatter->fFormatter.formatImpl(&result->fData, *ec);
 }
 
-U_DRAFT const UFormattedValue* U_EXPORT2
-unumf_resultAsFormattedValue(const UFormattedNumber* uresult, UErrorCode* ec) {
-    const auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
-    if (U_FAILURE(*ec)) { return nullptr; }
-
-    return static_cast<const UFormattedValueApiHelper*>(result)->exportConstForC();
-}
-
 U_CAPI int32_t U_EXPORT2
 unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t bufferCapacity,
                      UErrorCode* ec) {
@@ -206,13 +195,6 @@ unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPosition
     result->fImpl.getAllFieldPositions(*fpi, *ec);
 }
 
-U_CAPI void U_EXPORT2
-unumf_closeResult(UFormattedNumber* uresult) {
-    UErrorCode localStatus = U_ZERO_ERROR;
-    const UFormattedNumberImpl* impl = UFormattedNumberApiHelper::validate(uresult, localStatus);
-    delete impl;
-}
-
 U_CAPI void U_EXPORT2
 unumf_close(UNumberFormatter* f) {
     UErrorCode localStatus = U_ZERO_ERROR;
index ec3eebda01a0d95519bb2b94372eaab2d80e9a32..ab890012c7384ba8bb88661c3104e26cfca2c9cd 100644 (file)
@@ -684,14 +684,14 @@ LocalizedNumberFormatter::formatDecimalQuantity(const DecimalQuantity& dq, UErro
 
 void LocalizedNumberFormatter::formatImpl(impl::UFormattedNumberData* results, UErrorCode& status) const {
     if (computeCompiled(status)) {
-        fCompiled->format(results->quantity, results->string, status);
+        fCompiled->format(results->quantity, results->getStringRef(), status);
     } else {
-        NumberFormatterImpl::formatStatic(fMacros, results->quantity, results->string, status);
+        NumberFormatterImpl::formatStatic(fMacros, results->quantity, results->getStringRef(), status);
     }
     if (U_FAILURE(status)) {
         return;
     }
-    results->string.writeTerminator(status);
+    results->getStringRef().writeTerminator(status);
 }
 
 void LocalizedNumberFormatter::getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result,
index 378b7b075e22bad334a217427efea1bd5e561fb1..1e86f2f983366a73dd1e1109d4f52fd22335f22a 100644 (file)
@@ -14,79 +14,13 @@ U_NAMESPACE_BEGIN
 namespace number {
 
 
-FormattedNumber::FormattedNumber(FormattedNumber&& 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;
-}
-
-FormattedNumber& FormattedNumber::operator=(FormattedNumber&& 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;
-}
+UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumber)
 
-UnicodeString FormattedNumber::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();
-}
-
-UnicodeString FormattedNumber::toTempString(UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return ICU_Utility::makeBogusString();
-    }
-    if (fResults == nullptr) {
-        status = fErrorCode;
-        return ICU_Utility::makeBogusString();
-    }
-    return fResults->string.toTempUnicodeString();
-}
-
-Appendable& FormattedNumber::appendTo(Appendable& appendable, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return appendable;
-    }
-    if (fResults == nullptr) {
-        status = fErrorCode;
-        return appendable;
-    }
-    appendable.appendString(fResults->string.chars(), fResults->string.length());
-    return appendable;
-}
-
-UBool FormattedNumber::nextPosition(ConstrainedFieldPosition& cfpos, 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.nextPosition(cfpos, 0, status) ? TRUE : FALSE;
-}
 
 UBool FormattedNumber::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return FALSE;
-    }
-    if (fResults == nullptr) {
-        status = fErrorCode;
-        return FALSE;
-    }
+    UPRV_FORMATTED_VALUE_METHOD_GUARD(FALSE)
     // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
-    return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
+    return fData->getStringRef().nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
 }
 
 void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
@@ -96,30 +30,17 @@ void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErr
 
 void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih,
                                                UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    if (fResults == nullptr) {
-        status = fErrorCode;
-        return;
-    }
-    fResults->string.getAllFieldPositions(fpih, status);
+    UPRV_FORMATTED_VALUE_METHOD_GUARD()
+    fData->getStringRef().getAllFieldPositions(fpih, status);
 }
 
 void FormattedNumber::getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    if (fResults == nullptr) {
-        status = fErrorCode;
-        return;
-    }
-    output = fResults->quantity;
+    UPRV_FORMATTED_VALUE_METHOD_GUARD()
+    output = fData->quantity;
 }
 
-FormattedNumber::~FormattedNumber() {
-    delete fResults;
-}
+
+impl::UFormattedNumberData::~UFormattedNumberData() = default;
 
 
 } // namespace number
index 31fa647ee2d75ccfa3ae1b5e3ae10e3cff7edec3..88b493cbc254c9ce68e7153a71adcc5fb6ceeef5 100644 (file)
@@ -11,6 +11,7 @@
 #include "number_types.h"
 #include "number_decimalquantity.h"
 #include "number_stringbuilder.h"
+#include "formattedval_impl.h"
 
 U_NAMESPACE_BEGIN namespace number {
 namespace impl {
@@ -30,9 +31,12 @@ const DecimalQuantity* validateUFormattedNumberToDecimalQuantity(
  * The DecimalQuantity is not currently being used by FormattedNumber, but at some point it could be used
  * to add a toDecNumber() or similar method.
  */
-struct UFormattedNumberData : public UMemory {
+class UFormattedNumberData : public FormattedValueNumberStringBuilderImpl {
+public:
+    UFormattedNumberData() : FormattedValueNumberStringBuilderImpl(0) {}
+    virtual ~UFormattedNumberData();
+
     DecimalQuantity quantity;
-    NumberStringBuilder string;
 };
 
 
index 929cac996cf2414380acb25ef4e9c0c12187abf2..079d5194b03bcbccc536d88b5333fde8900c2ec1 100644 (file)
@@ -331,7 +331,7 @@ void LocalizedNumberRangeFormatter::formatImpl(
     if (U_FAILURE(status)) {
         return;
     }
-    results.string.writeTerminator(status);
+    results.getStringRef().writeTerminator(status);
 }
 
 const impl::NumberRangeFormatterImpl*
@@ -375,79 +375,13 @@ LocalizedNumberRangeFormatter::getFormatter(UErrorCode& status) const {
 }
 
 
-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();
-}
-
-UnicodeString FormattedNumberRange::toTempString(UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return ICU_Utility::makeBogusString();
-    }
-    if (fResults == nullptr) {
-        status = fErrorCode;
-        return ICU_Utility::makeBogusString();
-    }
-    return fResults->string.toTempUnicodeString();
-}
+UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumberRange)
 
-Appendable& FormattedNumberRange::appendTo(Appendable& appendable, UErrorCode& status) const {
-    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::nextPosition(ConstrainedFieldPosition& cfpos, 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.nextPosition(cfpos, 0, status) ? TRUE : FALSE;
-}
 
 UBool FormattedNumberRange::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return FALSE;
-    }
-    if (fResults == nullptr) {
-        status = fErrorCode;
-        return FALSE;
-    }
+    UPRV_FORMATTED_VALUE_METHOD_GUARD(FALSE)
     // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
-    return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
+    return fData->getStringRef().nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
 }
 
 void FormattedNumberRange::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
@@ -457,52 +391,27 @@ void FormattedNumberRange::getAllFieldPositions(FieldPositionIterator& iterator,
 
 void FormattedNumberRange::getAllFieldPositionsImpl(
         FieldPositionIteratorHandler& fpih, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    if (fResults == nullptr) {
-        status = fErrorCode;
-        return;
-    }
-    fResults->string.getAllFieldPositions(fpih, status);
+    UPRV_FORMATTED_VALUE_METHOD_GUARD()
+    fData->getStringRef().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();
+    UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString())
+    return fData->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();
+    UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString())
+    return fData->quantity2.toScientificString();
 }
 
 UNumberRangeIdentityResult FormattedNumberRange::getIdentityResult(UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return UNUM_IDENTITY_RESULT_NOT_EQUAL;
-    }
-    if (fResults == nullptr) {
-        status = fErrorCode;
-        return UNUM_IDENTITY_RESULT_NOT_EQUAL;
-    }
-    return fResults->identityResult;
+    UPRV_FORMATTED_VALUE_METHOD_GUARD(UNUM_IDENTITY_RESULT_NOT_EQUAL)
+    return fData->identityResult;
 }
 
-FormattedNumberRange::~FormattedNumberRange() {
-    delete fResults;
-}
+
+UFormattedNumberRangeData::~UFormattedNumberRangeData() = default;
 
 
 
index efea06019284526f14ca7c629eb0f5a78c28ae95..0cffb05628ddb3aa8d1e86025ca984c3cdf73ebe 100644 (file)
@@ -279,8 +279,8 @@ void NumberRangeFormatterImpl::formatSingleValue(UFormattedNumberRangeData& data
                                                  UErrorCode& status) const {
     if (U_FAILURE(status)) { return; }
     if (fSameFormatters) {
-        int32_t length = NumberFormatterImpl::writeNumber(micros1, data.quantity1, data.string, 0, status);
-        NumberFormatterImpl::writeAffixes(micros1, data.string, 0, length, status);
+        int32_t length = NumberFormatterImpl::writeNumber(micros1, data.quantity1, data.getStringRef(), 0, status);
+        NumberFormatterImpl::writeAffixes(micros1, data.getStringRef(), 0, length, status);
     } else {
         formatRange(data, micros1, micros2, status);
     }
@@ -292,12 +292,12 @@ void NumberRangeFormatterImpl::formatApproximately (UFormattedNumberRangeData& d
                                                     UErrorCode& status) const {
     if (U_FAILURE(status)) { return; }
     if (fSameFormatters) {
-        int32_t length = NumberFormatterImpl::writeNumber(micros1, data.quantity1, data.string, 0, status);
+        int32_t length = NumberFormatterImpl::writeNumber(micros1, data.quantity1, data.getStringRef(), 0, status);
         // HEURISTIC: Desired modifier order: inner, middle, approximately, outer.
-        length += micros1.modInner->apply(data.string, 0, length, status);
-        length += micros1.modMiddle->apply(data.string, 0, length, status);
-        length += fApproximatelyModifier.apply(data.string, 0, length, status);
-        micros1.modOuter->apply(data.string, 0, length, status);
+        length += micros1.modInner->apply(data.getStringRef(), 0, length, status);
+        length += micros1.modMiddle->apply(data.getStringRef(), 0, length, status);
+        length += fApproximatelyModifier.apply(data.getStringRef(), 0, length, status);
+        micros1.modOuter->apply(data.getStringRef(), 0, length, status);
     } else {
         formatRange(data, micros1, micros2, status);
     }
@@ -375,7 +375,7 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data,
             break;
     }
 
-    NumberStringBuilder& string = data.string;
+    NumberStringBuilder& string = data.getStringRef();
     int32_t lengthPrefix = 0;
     int32_t length1 = 0;
     int32_t lengthInfix = 0;
index 787fc656860d53c5593d0092c6b465dc8ad9276a..dc25dd4d67bfebf3e6005d540a8209dc7605494e 100644 (file)
@@ -14,6 +14,7 @@
 #include "number_decimalquantity.h"
 #include "number_formatimpl.h"
 #include "number_stringbuilder.h"
+#include "formattedval_impl.h"
 
 U_NAMESPACE_BEGIN namespace number {
 namespace impl {
@@ -24,20 +25,18 @@ namespace impl {
  *
  * Has incomplete magic number logic that will need to be finished
  * if this is to be exposed as C API in the future.
+ *
+ * Possible magic number: 0x46445200
+ * Reads in ASCII as "FDR" (FormatteDnumberRange with room at the end)
  */
-struct UFormattedNumberRangeData : public UMemory {
-    // The magic number to identify incoming objects.
-    // Reads in ASCII as "FDR" (FormatteDnumberRange with room at the end)
-    static constexpr int32_t kMagic = 0x46445200;
+class UFormattedNumberRangeData : public FormattedValueNumberStringBuilderImpl {
+public:
+    UFormattedNumberRangeData() : FormattedValueNumberStringBuilderImpl(0) {}
+    virtual ~UFormattedNumberRangeData();
 
-    // Data members:
-    int32_t fMagic = kMagic;
     DecimalQuantity quantity1;
     DecimalQuantity quantity2;
-    NumberStringBuilder string;
     UNumberRangeIdentityResult identityResult = UNUM_IDENTITY_RESULT_COUNT;
-
-    // No C conversion methods (no C API yet)
 };
 
 
index 2775766d32df801f1f3304d80ff6ee3bae44b9a8..678d91b9c824bc1068aa0be342e4c50cd5741eb9 100644 (file)
@@ -278,7 +278,7 @@ PluralFormat::format(const Formattable& numberObject, double number,
     auto *decFmt = dynamic_cast<DecimalFormat *>(numberFormat);
     if(decFmt != nullptr) {
         decFmt->toNumberFormatter().formatImpl(&data, status); // mutates &data
-        numberString = data.string.toUnicodeString();
+        numberString = data.getStringRef().toUnicodeString();
     } else {
         if (offset == 0) {
             numberFormat->format(numberObject, numberString, status);
index 7235fa6d9d6a1155ec28517289e0f395585fd220..9182f9e7d379a84f47ed11c5f283a4dbe3118343 100644 (file)
@@ -192,7 +192,7 @@ void QuantityFormatter::formatAndSelect(
         if (U_FAILURE(status)) {
             return;
         }
-        output = std::move(fn.string);
+        output = std::move(fn.getStringRef());
         pluralKeyword = rules.select(fn.quantity);
     } else {
         UnicodeString result;
index 3ee2a8df47654340c44c70578dac6cc412434a2c..8a95a02939ebbb9517c1f8508d9f162dea5d1a63 100644 (file)
@@ -2079,7 +2079,7 @@ SimpleDateFormat::zeroPaddingNumber(
         if (U_FAILURE(localStatus)) {
             return;
         }
-        appendTo.append(result.string.toTempUnicodeString());
+        appendTo.append(result.getStringRef().toTempUnicodeString());
         return;
     }
 
index e2f46850efe90d8c1bddf83e03e03ae8f4389eba..976a25764bef1e54dc323044be61631bb4808677 100644 (file)
@@ -40,7 +40,7 @@ class DateIntervalFormat;
 /**
  * An immutable class containing the result of a date interval formatting operation.
  *
- * Not intended for public subclassing.
+ * Instances of this class are immutable and thread-safe.
  *
  * When calling nextPosition():
  * The fields are returned from left to right. The special field category
@@ -50,6 +50,8 @@ class DateIntervalFormat;
  * corresponding fields in UFIELD_CATEGORY_DATE
  * in the nextPosition() iterator.
  *
+ * Not intended for public subclassing.
+ *
  * @draft ICU 64
  */
 class U_I18N_API FormattedDateInterval : public UMemory, public FormattedValue {
index bf956ba52c57021ad541ad082dc224b7b6174354..f4060a147b2f2bafd4eceba55d7eda779fe72040 100644 (file)
@@ -66,6 +66,8 @@ struct ListFormatData : public UMemory {
 /**
  * An immutable class containing the result of a list formatting operation.
  *
+ * Instances of this class are immutable and thread-safe.
+ *
  * When calling nextPosition():
  * The fields are returned from start to end. The special field category
  * UFIELD_CATEGORY_LIST_SPAN is used to indicate which argument
index 3ce74e29f54f71bcb374cc6e800d94ffe1864097..895ebd03ee955512d9ee4d197dd84a543bb1beba 100644 (file)
@@ -131,7 +131,7 @@ class Padder;
 struct MacroProps;
 struct MicroProps;
 class DecimalQuantity;
-struct UFormattedNumberData;
+class UFormattedNumberData;
 class NumberFormatterImpl;
 struct ParsedPatternInfo;
 class ScientificModifier;
@@ -2399,58 +2399,45 @@ class U_I18N_API FormattedNumber : public UMemory, public FormattedValue {
 
     /**
      * Default constructor; makes an empty FormattedNumber.
+     * @draft ICU 60
      */
     FormattedNumber()
-        : fResults(nullptr), fErrorCode(U_INVALID_STATE_ERROR) {};
-
-    /**
-     * Copying not supported; use move constructor instead.
-     */
-    FormattedNumber(const FormattedNumber&) = delete;
+        : fData(nullptr), fErrorCode(U_INVALID_STATE_ERROR) {};
 
     /**
-     * Move constructor:
-     * Leaves the source FormattedNumber in an undefined state.
+     * Move constructor: Leaves the source FormattedNumber in an undefined state.
      * @draft ICU 62
      */
     FormattedNumber(FormattedNumber&& src) U_NOEXCEPT;
 
     /**
-     * Destruct an instance of FormattedNumber, cleaning up any memory it might own.
+     * Destruct an instance of FormattedNumber.
      * @draft ICU 60
      */
     virtual ~FormattedNumber() U_OVERRIDE;
 
-    /**
-     * Copying not supported; use move assignment instead.
-     */
+    /** Copying not supported; use move constructor instead. */
+    FormattedNumber(const FormattedNumber&) = delete;
+
+    /** Copying not supported; use move assignment instead. */
     FormattedNumber& operator=(const FormattedNumber&) = delete;
 
     /**
-     * Move assignment:
-     * Leaves the source FormattedNumber in an undefined state.
+     * Move assignment: Leaves the source FormattedNumber in an undefined state.
      * @draft ICU 62
      */
     FormattedNumber& operator=(FormattedNumber&& src) U_NOEXCEPT;
 
-    /**
-     * @copydoc FormattedValue::toString()
-     */
+    /** @copydoc FormattedValue::toString() */
     UnicodeString toString(UErrorCode& status) const U_OVERRIDE;
 
-    /**
-     * @copydoc FormattedValue::toTempString()
-     */
+    /** @copydoc FormattedValue::toTempString() */
     UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE;
 
-    /**
-     * @copydoc FormattedValue::appendTo()
-     */
+    /** @copydoc FormattedValue::appendTo() */
     Appendable &appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE;
 
-    /**
-     * @copydoc FormattedValue::nextPosition()
-     */
+    /** @copydoc FormattedValue::nextPosition() */
     UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE;
 
     /**
@@ -2523,7 +2510,7 @@ class U_I18N_API FormattedNumber : public UMemory, public FormattedValue {
 
   private:
     // Can't use LocalPointer because UFormattedNumberData is forward-declared
-    impl::UFormattedNumberData *fResults;
+    const impl::UFormattedNumberData *fData;
 
     // Error code for the terminal methods
     UErrorCode fErrorCode;
@@ -2533,10 +2520,10 @@ class U_I18N_API FormattedNumber : public UMemory, public FormattedValue {
      * @internal
      */
     explicit FormattedNumber(impl::UFormattedNumberData *results)
-        : fResults(results), fErrorCode(U_ZERO_ERROR) {};
+        : fData(results), fErrorCode(U_ZERO_ERROR) {};
 
     explicit FormattedNumber(UErrorCode errorCode)
-        : fResults(nullptr), fErrorCode(errorCode) {};
+        : fData(nullptr), fErrorCode(errorCode) {};
 
     // To give LocalizedNumberFormatter format methods access to this class's constructor:
     friend class LocalizedNumberFormatter;
index 3cbf290edac01e50e125e0ac88cdb8b8138e4a4c..b23eb9f6d581e6e6a2bca9d4f393acc2ab94ccb7 100644 (file)
@@ -176,7 +176,7 @@ namespace impl {
 // Forward declarations:
 struct RangeMacroProps;
 class DecimalQuantity;
-struct UFormattedNumberRangeData;
+class UFormattedNumberRangeData;
 class NumberRangeFormatterImpl;
 
 } // namespace impl
@@ -828,7 +828,7 @@ class U_I18N_API FormattedNumberRange : public UMemory, public FormattedValue {
 
   private:
     // Can't use LocalPointer because UFormattedNumberRangeData is forward-declared
-    const impl::UFormattedNumberRangeData *fResults;
+    const impl::UFormattedNumberRangeData *fData;
 
     // Error code for the terminal methods
     UErrorCode fErrorCode;
@@ -838,10 +838,10 @@ class U_I18N_API FormattedNumberRange : public UMemory, public FormattedValue {
      * @internal
      */
     explicit FormattedNumberRange(impl::UFormattedNumberRangeData *results)
-        : fResults(results), fErrorCode(U_ZERO_ERROR) {};
+        : fData(results), fErrorCode(U_ZERO_ERROR) {};
 
     explicit FormattedNumberRange(UErrorCode errorCode)
-        : fResults(nullptr), fErrorCode(errorCode) {};
+        : fData(nullptr), fErrorCode(errorCode) {};
 
     void getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, UErrorCode& status) const;
 
index 995fa2dbaee3079c77fe5ce1e925fe92ebd1f8e3..73202652e06ddd9dcc2eb6eca2389959690a42ad 100644 (file)
@@ -252,6 +252,8 @@ class FormattedRelativeDateTimeData;
 /**
  * An immutable class containing the result of a relative datetime formatting operation.
  *
+ * Instances of this class are immutable and thread-safe.
+ *
  * Not intended for public subclassing.
  *
  * @draft ICU 64
index 7b520cbff81af9fea744af79653c60d8624f58a6..c33255422e6195e74e69dd48c1e99fd893260b23 100644 (file)
@@ -473,11 +473,9 @@ unumf_openForSkeletonAndLocaleWithError(
 
 
 /**
- * Creates a new UFormattedNumber for holding the result of a number formatting operation.
- *
- * Objects of type UFormattedNumber are not guaranteed to be threadsafe.
- *
- * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
+ * Creates an object to hold the result of a UNumberFormatter
+ * operation. The object can be used repeatedly; it is cleared whenever
+ * passed to a format function.
  *
  * @param ec Set if an error occurs.
  * @draft ICU 62
@@ -565,7 +563,7 @@ unumf_formatDecimal(const UNumberFormatter* uformatter, const char* value, int32
  * @draft ICU 64
  */
 U_DRAFT const UFormattedValue* U_EXPORT2
-unumf_resultAsFormattedValue(const UFormattedNumber* uresult, UErrorCode* ec);
+unumf_resultAsValue(const UFormattedNumber* uresult, UErrorCode* ec);
 
 
 /**
@@ -576,7 +574,7 @@ unumf_resultAsFormattedValue(const UFormattedNumber* uresult, UErrorCode* ec);
  * Also see ufmtval_getString, which returns a NUL-terminated string:
  *
  *     int32_t len;
- *     const UChar* str = ufmtval_getString(unumf_resultAsFormattedValue(uresult, &ec), &len, &ec);
+ *     const UChar* str = ufmtval_getString(unumf_resultAsValue(uresult, &ec), &len, &ec);
  *
  * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
  *
@@ -658,8 +656,6 @@ unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPosition
 /**
  * Releases the UNumberFormatter created by unumf_openForSkeletonAndLocale().
  *
- * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
- *
  * @param uformatter An object created by unumf_openForSkeletonAndLocale().
  * @draft ICU 62
  */
@@ -670,8 +666,6 @@ unumf_close(UNumberFormatter* uformatter);
 /**
  * Releases the UFormattedNumber created by unumf_openResult().
  *
- * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
- *
  * @param uresult An object created by unumf_openResult().
  * @draft ICU 62
  */
index 59e65f2afdb57e23291707658dcaa0fedafa1b12..b45065000984fd215c1722a620f4b5274c895cbd 100644 (file)
@@ -207,7 +207,7 @@ static void TestFormattedValue() {
 
     unumf_formatInt(uformatter, 55000, uresult, &ec); // "55.00 K"
     if (assertSuccessCheck("Should format without error", &ec, TRUE)) {
-        const UFormattedValue* fv = unumf_resultAsFormattedValue(uresult, &ec);
+        const UFormattedValue* fv = unumf_resultAsValue(uresult, &ec);
         assertSuccess("Should convert without error", &ec);
         static const UFieldPosition expectedFieldPositions[] = {
             // field, begin index, end index
index 1e519808e7533f04916bd7814ae1e31103902171..466e6427f957d6ac210bf5671dfed644e44480e8 100644 (file)
@@ -949,7 +949,7 @@ group: number_output
     standardplural.o plurrule.o
   deps
     # FormattedNumber internals:
-    number_representation format
+    number_representation format formatted_value_sbimpl
     # PluralRules internals:
     unifiedcache
 
index 12621f3ca019b03fbb9fbcaefa66d24d77313a7e..3f0944d63b6f1f310cca4ebaf16ca3629937b815 100644 (file)
@@ -17,6 +17,7 @@ import com.ibm.icu.lang.UCharacter;
 import com.ibm.icu.text.Replaceable;
 import com.ibm.icu.text.UTF16;
 import com.ibm.icu.text.UnicodeMatcher;
+import com.ibm.icu.util.ICUUncheckedIOException;
 
 public final class Utility {
 
@@ -1845,4 +1846,16 @@ public final class Utility {
         }
         return hash;
     }
+
+    /**
+     * Appends a CharSequence to an Appendable, converting IOException to ICUUncheckedIOException.
+     */
+    public static <A extends Appendable> A appendTo(CharSequence string, A appendable) {
+        try {
+            appendable.append(string);
+            return appendable;
+        } catch (IOException e) {
+            throw new ICUUncheckedIOException(e);
+        }
+    }
 }
index 09da4247e37f2409c7a174ab9b2003d76fe8a674..75fa46e6f447d21dcd205cfa7ba44774f2957b8d 100644 (file)
@@ -2,18 +2,17 @@
 // License & terms of use: http://www.unicode.org/copyright.html#License
 package com.ibm.icu.number;
 
-import java.io.IOException;
 import java.math.BigDecimal;
 import java.text.AttributedCharacterIterator;
 import java.text.FieldPosition;
 import java.util.Arrays;
 
+import com.ibm.icu.impl.Utility;
 import com.ibm.icu.impl.number.DecimalQuantity;
 import com.ibm.icu.impl.number.NumberStringBuilder;
 import com.ibm.icu.text.ConstrainedFieldPosition;
 import com.ibm.icu.text.FormattedValue;
 import com.ibm.icu.text.PluralRules.IFixedDecimal;
-import com.ibm.icu.util.ICUUncheckedIOException;
 
 /**
  * The result of a number formatting operation. This class allows the result to be exported in several
@@ -24,11 +23,11 @@ import com.ibm.icu.util.ICUUncheckedIOException;
  * @see NumberFormatter
  */
 public class FormattedNumber implements FormattedValue {
-    final NumberStringBuilder nsb;
+    final NumberStringBuilder string;
     final DecimalQuantity fq;
 
     FormattedNumber(NumberStringBuilder nsb, DecimalQuantity fq) {
-        this.nsb = nsb;
+        this.string = nsb;
         this.fq = fq;
     }
 
@@ -40,24 +39,18 @@ public class FormattedNumber implements FormattedValue {
      */
     @Override
     public String toString() {
-        return nsb.toString();
+        return string.toString();
     }
 
     /**
      * {@inheritDoc}
      *
-     * @return The same Appendable, for chaining.
-     * @draft ICU 60
+     * @draft ICU 64
+     * @provisional This API might change or be removed in a future release.
      */
     @Override
-    public <A extends Appendable> A appendTo(A appendable) {
-        try {
-            appendable.append(nsb);
-        } catch (IOException e) {
-            // Throw as an unchecked exception to avoid users needing try/catch
-            throw new ICUUncheckedIOException(e);
-        }
-        return appendable;
+    public int length() {
+        return string.length();
     }
 
     /**
@@ -68,7 +61,7 @@ public class FormattedNumber implements FormattedValue {
      */
     @Override
     public char charAt(int index) {
-        return nsb.charAt(index);
+        return string.charAt(index);
     }
 
     /**
@@ -78,19 +71,19 @@ public class FormattedNumber implements FormattedValue {
      * @provisional This API might change or be removed in a future release.
      */
     @Override
-    public int length() {
-        return nsb.length();
+    public CharSequence subSequence(int start, int end) {
+        return string.subString(start, end);
     }
 
     /**
      * {@inheritDoc}
      *
-     * @draft ICU 64
+     * @draft ICU 60
      * @provisional This API might change or be removed in a future release.
      */
     @Override
-    public CharSequence subSequence(int start, int end) {
-        return nsb.subString(start, end);
+    public <A extends Appendable> A appendTo(A appendable) {
+        return Utility.appendTo(string, appendable);
     }
 
     /**
@@ -101,7 +94,18 @@ public class FormattedNumber implements FormattedValue {
      */
     @Override
     public boolean nextPosition(ConstrainedFieldPosition cfpos) {
-        return nsb.nextPosition(cfpos, null);
+        return string.nextPosition(cfpos, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     */
+    @Override
+    public AttributedCharacterIterator toCharacterIterator() {
+        return string.toCharacterIterator(null);
     }
 
     /**
@@ -139,18 +143,7 @@ public class FormattedNumber implements FormattedValue {
      */
     public boolean nextFieldPosition(FieldPosition fieldPosition) {
         fq.populateUFieldPosition(fieldPosition);
-        return nsb.nextFieldPosition(fieldPosition);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @draft ICU 62
-     * @provisional This API might change or be removed in a future release.
-     */
-    @Override
-    public AttributedCharacterIterator toCharacterIterator() {
-        return nsb.toCharacterIterator(null);
+        return string.nextFieldPosition(fieldPosition);
     }
 
     /**
@@ -186,8 +179,8 @@ public class FormattedNumber implements FormattedValue {
     public int hashCode() {
         // NumberStringBuilder and BigDecimal are mutable, so we can't call
         // #equals() or #hashCode() on them directly.
-        return Arrays.hashCode(nsb.toCharArray())
-                ^ Arrays.hashCode(nsb.toFieldArray())
+        return Arrays.hashCode(string.toCharArray())
+                ^ Arrays.hashCode(string.toFieldArray())
                 ^ fq.toBigDecimal().hashCode();
     }
 
@@ -208,8 +201,8 @@ public class FormattedNumber implements FormattedValue {
         // NumberStringBuilder and BigDecimal are mutable, so we can't call
         // #equals() or #hashCode() on them directly.
         FormattedNumber _other = (FormattedNumber) other;
-        return Arrays.equals(nsb.toCharArray(), _other.nsb.toCharArray())
-                && Arrays.equals(nsb.toFieldArray(), _other.nsb.toFieldArray())
+        return Arrays.equals(string.toCharArray(), _other.string.toCharArray())
+                && Arrays.equals(string.toFieldArray(), _other.string.toFieldArray())
                 && fq.toBigDecimal().equals(_other.fq.toBigDecimal());
     }
 }
\ No newline at end of file
index f451fdfa455d0c160fab4375ac3b2e82735a461a..a8e063b9ecca9aca9d7c00bc66312ad1c169d395 100644 (file)
@@ -448,6 +448,10 @@ public final class RelativeDateTimeFormatter {
      * Represents the result of a formatting operation of a relative datetime.
      * Access the string value or field information.
      *
+     * Instances of this class are immutable and thread-safe.
+     *
+     * Not intended for public subclassing.
+     *
      * @author sffc
      * @draft ICU 64
      * @provisional This API might change or be removed in a future release.