From: Shane Carr Date: Fri, 15 Feb 2019 06:42:09 +0000 (-0800) Subject: ICU-20409 Additional refactoring of FormattedValue implementations. X-Git-Tag: release-64-rc~77 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=35b182767f6387054ce436aba68f54abe1a927bb;p=icu ICU-20409 Additional refactoring of FormattedValue implementations. - Migrates Number[Range] formatter to helper macros. - Adds additional macros. - Syncs docstrings between subclasses. --- diff --git a/icu4c/source/i18n/formattedval_impl.h b/icu4c/source/i18n/formattedval_impl.h index 5289f285c3f..ee15ed9dfd0 100644 --- a/icu4c/source/i18n/formattedval_impl.h +++ b/icu4c/source/i18n/formattedval_impl.h @@ -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(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(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(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(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 diff --git a/icu4c/source/i18n/number_asformat.cpp b/icu4c/source/i18n/number_asformat.cpp index c6bb538932c..9d3ea69f578 100644 --- a/icu4c/source/i18n/number_asformat.cpp +++ b/icu4c/source/i18n/number_asformat.cpp @@ -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; } diff --git a/icu4c/source/i18n/number_capi.cpp b/icu4c/source/i18n/number_capi.cpp index e67dd90fce7..f802e211049 100644 --- a/icu4c/source/i18n/number_capi.cpp +++ b/icu4c/source/i18n/number_capi.cpp @@ -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(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(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; diff --git a/icu4c/source/i18n/number_fluent.cpp b/icu4c/source/i18n/number_fluent.cpp index ec3eebda01a..ab890012c73 100644 --- a/icu4c/source/i18n/number_fluent.cpp +++ b/icu4c/source/i18n/number_fluent.cpp @@ -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, diff --git a/icu4c/source/i18n/number_output.cpp b/icu4c/source/i18n/number_output.cpp index 378b7b075e2..1e86f2f9833 100644 --- a/icu4c/source/i18n/number_output.cpp +++ b/icu4c/source/i18n/number_output.cpp @@ -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 diff --git a/icu4c/source/i18n/number_utypes.h b/icu4c/source/i18n/number_utypes.h index 31fa647ee2d..88b493cbc25 100644 --- a/icu4c/source/i18n/number_utypes.h +++ b/icu4c/source/i18n/number_utypes.h @@ -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; }; diff --git a/icu4c/source/i18n/numrange_fluent.cpp b/icu4c/source/i18n/numrange_fluent.cpp index 929cac996cf..079d5194b03 100644 --- a/icu4c/source/i18n/numrange_fluent.cpp +++ b/icu4c/source/i18n/numrange_fluent.cpp @@ -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; diff --git a/icu4c/source/i18n/numrange_impl.cpp b/icu4c/source/i18n/numrange_impl.cpp index efea0601928..0cffb05628d 100644 --- a/icu4c/source/i18n/numrange_impl.cpp +++ b/icu4c/source/i18n/numrange_impl.cpp @@ -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; diff --git a/icu4c/source/i18n/numrange_impl.h b/icu4c/source/i18n/numrange_impl.h index 787fc656860..dc25dd4d67b 100644 --- a/icu4c/source/i18n/numrange_impl.h +++ b/icu4c/source/i18n/numrange_impl.h @@ -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) }; diff --git a/icu4c/source/i18n/plurfmt.cpp b/icu4c/source/i18n/plurfmt.cpp index 2775766d32d..678d91b9c82 100644 --- a/icu4c/source/i18n/plurfmt.cpp +++ b/icu4c/source/i18n/plurfmt.cpp @@ -278,7 +278,7 @@ PluralFormat::format(const Formattable& numberObject, double number, auto *decFmt = dynamic_cast(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); diff --git a/icu4c/source/i18n/quantityformatter.cpp b/icu4c/source/i18n/quantityformatter.cpp index 7235fa6d9d6..9182f9e7d37 100644 --- a/icu4c/source/i18n/quantityformatter.cpp +++ b/icu4c/source/i18n/quantityformatter.cpp @@ -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; diff --git a/icu4c/source/i18n/smpdtfmt.cpp b/icu4c/source/i18n/smpdtfmt.cpp index 3ee2a8df476..8a95a02939e 100644 --- a/icu4c/source/i18n/smpdtfmt.cpp +++ b/icu4c/source/i18n/smpdtfmt.cpp @@ -2079,7 +2079,7 @@ SimpleDateFormat::zeroPaddingNumber( if (U_FAILURE(localStatus)) { return; } - appendTo.append(result.string.toTempUnicodeString()); + appendTo.append(result.getStringRef().toTempUnicodeString()); return; } diff --git a/icu4c/source/i18n/unicode/dtitvfmt.h b/icu4c/source/i18n/unicode/dtitvfmt.h index e2f46850efe..976a25764be 100644 --- a/icu4c/source/i18n/unicode/dtitvfmt.h +++ b/icu4c/source/i18n/unicode/dtitvfmt.h @@ -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 { diff --git a/icu4c/source/i18n/unicode/listformatter.h b/icu4c/source/i18n/unicode/listformatter.h index bf956ba52c5..f4060a147b2 100644 --- a/icu4c/source/i18n/unicode/listformatter.h +++ b/icu4c/source/i18n/unicode/listformatter.h @@ -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 diff --git a/icu4c/source/i18n/unicode/numberformatter.h b/icu4c/source/i18n/unicode/numberformatter.h index 3ce74e29f54..895ebd03ee9 100644 --- a/icu4c/source/i18n/unicode/numberformatter.h +++ b/icu4c/source/i18n/unicode/numberformatter.h @@ -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; diff --git a/icu4c/source/i18n/unicode/numberrangeformatter.h b/icu4c/source/i18n/unicode/numberrangeformatter.h index 3cbf290edac..b23eb9f6d58 100644 --- a/icu4c/source/i18n/unicode/numberrangeformatter.h +++ b/icu4c/source/i18n/unicode/numberrangeformatter.h @@ -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; diff --git a/icu4c/source/i18n/unicode/reldatefmt.h b/icu4c/source/i18n/unicode/reldatefmt.h index 995fa2dbaee..73202652e06 100644 --- a/icu4c/source/i18n/unicode/reldatefmt.h +++ b/icu4c/source/i18n/unicode/reldatefmt.h @@ -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 diff --git a/icu4c/source/i18n/unicode/unumberformatter.h b/icu4c/source/i18n/unicode/unumberformatter.h index 7b520cbff81..c33255422e6 100644 --- a/icu4c/source/i18n/unicode/unumberformatter.h +++ b/icu4c/source/i18n/unicode/unumberformatter.h @@ -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 */ diff --git a/icu4c/source/test/cintltst/unumberformattertst.c b/icu4c/source/test/cintltst/unumberformattertst.c index 59e65f2afdb..b4506500098 100644 --- a/icu4c/source/test/cintltst/unumberformattertst.c +++ b/icu4c/source/test/cintltst/unumberformattertst.c @@ -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 diff --git a/icu4c/source/test/depstest/dependencies.txt b/icu4c/source/test/depstest/dependencies.txt index 1e519808e75..466e6427f95 100644 --- a/icu4c/source/test/depstest/dependencies.txt +++ b/icu4c/source/test/depstest/dependencies.txt @@ -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 diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/Utility.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/Utility.java index 12621f3ca01..3f0944d63b6 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/Utility.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/Utility.java @@ -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 appendTo(CharSequence string, A appendable) { + try { + appendable.append(string); + return appendable; + } catch (IOException e) { + throw new ICUUncheckedIOException(e); + } + } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumber.java b/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumber.java index 09da4247e37..75fa46e6f44 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumber.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumber.java @@ -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 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 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 diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/RelativeDateTimeFormatter.java b/icu4j/main/classes/core/src/com/ibm/icu/text/RelativeDateTimeFormatter.java index f451fdfa455..a8e063b9ecc 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/RelativeDateTimeFormatter.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/RelativeDateTimeFormatter.java @@ -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.