From 37e589fb3a20f5547be683acf68e0d31f37359f8 Mon Sep 17 00:00:00 2001 From: "Shane F. Carr" Date: Mon, 4 Feb 2019 22:24:00 -0800 Subject: [PATCH] ICU-20121 Initial UFormattedList implementation, C. --- icu4c/source/i18n/formattedval_iterimpl.cpp | 6 +- icu4c/source/i18n/ulistformatter.cpp | 98 ++++++++++++++++----- icu4c/source/i18n/unicode/ulistformatter.h | 91 +++++++++++++++++++ icu4c/source/test/cintltst/ulistfmttest.c | 74 ++++++++++++++++ 4 files changed, 244 insertions(+), 25 deletions(-) diff --git a/icu4c/source/i18n/formattedval_iterimpl.cpp b/icu4c/source/i18n/formattedval_iterimpl.cpp index fc981b3b2b9..35ce55c8b6d 100644 --- a/icu4c/source/i18n/formattedval_iterimpl.cpp +++ b/icu4c/source/i18n/formattedval_iterimpl.cpp @@ -29,9 +29,9 @@ UnicodeString FormattedValueFieldPositionIteratorImpl::toString( UnicodeString FormattedValueFieldPositionIteratorImpl::toTempString( UErrorCode&) const { - UnicodeString ret; - ret.fastCopyFrom(fString); - return ret; + // The alias must point to memory owned by this object; + // fastCopyFrom doesn't do this when using a stack buffer. + return UnicodeString(TRUE, fString.getBuffer(), fString.length()); } Appendable& FormattedValueFieldPositionIteratorImpl::appendTo( diff --git a/icu4c/source/i18n/ulistformatter.cpp b/icu4c/source/i18n/ulistformatter.cpp index c140c784b52..e011422b656 100644 --- a/icu4c/source/i18n/ulistformatter.cpp +++ b/icu4c/source/i18n/ulistformatter.cpp @@ -15,6 +15,7 @@ #include "unicode/listformatter.h" #include "unicode/localpointer.h" #include "cmemory.h" +#include "formattedval_impl.h" U_NAMESPACE_USE @@ -40,6 +41,49 @@ ulistfmt_close(UListFormatter *listfmt) } +// Magic number: FLST in ASCII +UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL( + FormattedList, + UFormattedList, + UFormattedListImpl, + UFormattedListApiHelper, + ulistfmt, + 0x464C5354) + + +static UnicodeString* getUnicodeStrings( + const UChar* const strings[], + const int32_t* stringLengths, + int32_t stringCount, + UnicodeString* length4StackBuffer, + LocalArray& maybeOwner, + UErrorCode& status) { + U_ASSERT(U_SUCCESS(status)); + if (stringCount < 0 || (strings == NULL && stringCount > 0)) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return nullptr; + } + UnicodeString* ustrings = length4StackBuffer; + if (stringCount > 4) { + maybeOwner.adoptInsteadAndCheckErrorCode(new UnicodeString[stringCount], status); + if (U_FAILURE(status)) { + return nullptr; + } + ustrings = maybeOwner.getAlias(); + } + if (stringLengths == NULL) { + for (int32_t stringIndex = 0; stringIndex < stringCount; stringIndex++) { + ustrings[stringIndex].setTo(TRUE, strings[stringIndex], -1); + } + } else { + for (int32_t stringIndex = 0; stringIndex < stringCount; stringIndex++) { + ustrings[stringIndex].setTo(stringLengths[stringIndex] < 0, strings[stringIndex], stringLengths[stringIndex]); + } + } + return ustrings; +} + + U_CAPI int32_t U_EXPORT2 ulistfmt_format(const UListFormatter* listfmt, const UChar* const strings[], @@ -52,27 +96,16 @@ ulistfmt_format(const UListFormatter* listfmt, if (U_FAILURE(*status)) { return -1; } - if (stringCount < 0 || (strings == NULL && stringCount > 0) || ((result == NULL)? resultCapacity != 0 : resultCapacity < 0)) { + if ((result == NULL) ? resultCapacity != 0 : resultCapacity < 0) { *status = U_ILLEGAL_ARGUMENT_ERROR; return -1; } - UnicodeString ustringsStackBuf[4]; - UnicodeString* ustrings = ustringsStackBuf; - if (stringCount > UPRV_LENGTHOF(ustringsStackBuf)) { - ustrings = new UnicodeString[stringCount]; - if (ustrings == NULL) { - *status = U_MEMORY_ALLOCATION_ERROR; - return -1; - } - } - if (stringLengths == NULL) { - for (int32_t stringIndex = 0; stringIndex < stringCount; stringIndex++) { - ustrings[stringIndex].setTo(TRUE, strings[stringIndex], -1); - } - } else { - for (int32_t stringIndex = 0; stringIndex < stringCount; stringIndex++) { - ustrings[stringIndex].setTo(stringLengths[stringIndex] < 0, strings[stringIndex], stringLengths[stringIndex]); - } + UnicodeString length4StackBuffer[4]; + LocalArray maybeOwner; + UnicodeString* ustrings = getUnicodeStrings( + strings, stringLengths, stringCount, length4StackBuffer, maybeOwner, *status); + if (U_FAILURE(*status)) { + return -1; } UnicodeString res; if (result != NULL) { @@ -80,12 +113,33 @@ ulistfmt_format(const UListFormatter* listfmt, // otherwise, alias the destination buffer (copied from udat_format) res.setTo(result, 0, resultCapacity); } - ((const ListFormatter*)listfmt)->format( ustrings, stringCount, res, *status ); - if (ustrings != ustringsStackBuf) { - delete[] ustrings; - } + reinterpret_cast(listfmt)->format( ustrings, stringCount, res, *status ); return res.extract(result, resultCapacity, *status); } +U_CAPI void U_EXPORT2 +ulistfmt_formatStringsToValue( + const UListFormatter* listfmt, + const UChar* const strings[], + const int32_t * stringLengths, + int32_t stringCount, + UFormattedList* uresult, + UErrorCode* status) { + auto* result = UFormattedListApiHelper::validate(uresult, *status); + if (U_FAILURE(*status)) { + return; + } + UnicodeString length4StackBuffer[4]; + LocalArray maybeOwner; + UnicodeString* ustrings = getUnicodeStrings( + strings, stringLengths, stringCount, length4StackBuffer, maybeOwner, *status); + if (U_FAILURE(*status)) { + return; + } + result->fImpl = reinterpret_cast(listfmt) + ->formatStringsToValue(ustrings, stringCount, *status); +} + + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/unicode/ulistformatter.h b/icu4c/source/i18n/unicode/ulistformatter.h index d794a894047..1321ee119a6 100644 --- a/icu4c/source/i18n/unicode/ulistformatter.h +++ b/icu4c/source/i18n/unicode/ulistformatter.h @@ -15,6 +15,7 @@ #if !UCONFIG_NO_FORMATTING #include "unicode/localpointer.h" +#include "unicode/uformattedvalue.h" /** * \file @@ -33,6 +34,13 @@ struct UListFormatter; typedef struct UListFormatter UListFormatter; /**< C typedef for struct UListFormatter. @stable ICU 55 */ +struct UFormattedList; +/** + * Opaque struct to contain the results of a UListFormatter operation. + * @draft ICU 64 + */ +typedef struct UFormattedList UFormattedList; + #ifndef U_HIDE_DRAFT_API /** * FieldPosition and UFieldPosition selectors for format fields @@ -82,6 +90,44 @@ ulistfmt_open(const char* locale, U_CAPI void U_EXPORT2 ulistfmt_close(UListFormatter *listfmt); +/** + * Creates an object to hold the result of a UListFormatter + * operation. The object can be used repeatedly; it is cleared whenever + * passed to a format function. + * + * @param ec Set if an error occurs. + * @return A pointer needing ownership. + * @draft ICU 64 + */ +U_CAPI UFormattedList* U_EXPORT2 +ulistfmt_openResult(UErrorCode* ec); + +/** + * Returns a representation of a UFormattedList as a UFormattedValue, + * which can be subsequently passed to any API requiring that type. + * + * The returned object is owned by the UFormattedList and is valid + * only as long as the UFormattedList is present and unchanged in memory. + * + * You can think of this method as a cast between types. + * + * @param uresult The object containing the formatted string. + * @param ec Set if an error occurs. + * @return A UFormattedValue owned by the input object. + * @draft ICU 64 + */ +U_CAPI const UFormattedValue* U_EXPORT2 +ulistfmt_resultAsValue(const UFormattedList* uresult, UErrorCode* ec); + +/** + * Releases the UFormattedList created by ulistfmt_openResult(). + * + * @param uresult The object to release. + * @draft ICU 64 + */ +U_CAPI void U_EXPORT2 +ulistfmt_closeResult(UFormattedList* uresult); + #if U_SHOW_CPLUSPLUS_API @@ -98,6 +144,17 @@ U_NAMESPACE_BEGIN */ U_DEFINE_LOCAL_OPEN_POINTER(LocalUListFormatterPointer, UListFormatter, ulistfmt_close); +/** + * \class LocalUFormattedListPointer + * "Smart pointer" class, closes a UFormattedList via ulistfmt_closeResult(). + * For most methods see the LocalPointerBase base class. + * + * @see LocalPointerBase + * @see LocalPointer + * @stable ICU 55 + */ +U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedListPointer, UFormattedList, ulistfmt_closeResult); + U_NAMESPACE_END #endif @@ -145,6 +202,40 @@ ulistfmt_format(const UListFormatter* listfmt, int32_t resultCapacity, UErrorCode* status); +/** + * Formats a list of strings to a UFormattedList, which exposes more + * information than the string exported by ulistfmt_format(). + * + * @param listfmt + * The UListFormatter object specifying the list conventions. + * @param strings + * An array of pointers to UChar strings; the array length is + * specified by stringCount. Must be non-NULL if stringCount > 0. + * @param stringLengths + * An array of string lengths corresponding to the strings[] + * parameter; any individual length value may be negative to indicate + * that the corresponding strings[] entry is 0-terminated, or + * stringLengths itself may be NULL if all of the strings are + * 0-terminated. If non-NULL, the stringLengths array must have + * stringCount entries. + * @param stringCount + * the number of entries in strings[], and the number of entries + * in the stringLengths array if it is not NULL. Must be >= 0. + * @param uresult + * The object in which to store the result of the list formatting + * operation. See ulistfmt_openResult(). + * @param status + * Error code set if an error occurred during formatting. + */ +U_CAPI void U_EXPORT2 +ulistfmt_formatStringsToValue( + const UListFormatter* listfmt, + const UChar* const strings[], + const int32_t * stringLengths, + int32_t stringCount, + UFormattedList* uresult, + UErrorCode* status); + #endif /* #if !UCONFIG_NO_FORMATTING */ #endif diff --git a/icu4c/source/test/cintltst/ulistfmttest.c b/icu4c/source/test/cintltst/ulistfmttest.c index 165bff431d2..e2e5a80c56e 100644 --- a/icu4c/source/test/cintltst/ulistfmttest.c +++ b/icu4c/source/test/cintltst/ulistfmttest.c @@ -15,8 +15,10 @@ #include "cintltst.h" #include "cmemory.h" #include "cstring.h" +#include "cformtst.h" static void TestUListFmt(void); +static void TestUListFmtToValue(void); void addUListFmtTest(TestNode** root); @@ -25,6 +27,7 @@ void addUListFmtTest(TestNode** root); void addUListFmtTest(TestNode** root) { TESTCASE(TestUListFmt); + TESTCASE(TestUListFmtToValue); } static const UChar str0[] = { 0x41,0 }; /* "A" */ @@ -126,5 +129,76 @@ static void TestUListFmt() { } } +static void TestUListFmtToValue() { + UErrorCode ec = U_ZERO_ERROR; + UListFormatter* fmt = ulistfmt_open("en", &ec); + UFormattedList* fl = ulistfmt_openResult(&ec); + assertSuccess("Opening", &ec); + + { + const char* message = "Field position test 1"; + const UChar* expectedString = u"hello, wonderful, and world"; + const UChar* inputs[] = { + u"hello", + u"wonderful", + u"world" + }; + ulistfmt_formatStringsToValue(fmt, inputs, NULL, UPRV_LENGTHOF(inputs), fl, &ec); + assertSuccess("Formatting", &ec); + static const UFieldPositionWithCategory expectedFieldPositions[] = { + // field, begin index, end index + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 0, 5}, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 7, 16}, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 22, 27}, + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD, 5, 7}, + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD, 16, 22}}; + checkMixedFormattedValue( + message, + ulistfmt_resultAsValue(fl, &ec), + expectedString, + expectedFieldPositions, + UPRV_LENGTHOF(expectedFieldPositions)); + } + { + const char* message = "Field position test 1"; + const UChar* expectedString = u"A, B, C, D, E, F, and G"; + const UChar* inputs[] = { + u"A", + u"B", + u"C", + u"D", + u"E", + u"F", + u"G" + }; + ulistfmt_formatStringsToValue(fmt, inputs, NULL, UPRV_LENGTHOF(inputs), fl, &ec); + assertSuccess("Formatting", &ec); + static const UFieldPositionWithCategory expectedFieldPositions[] = { + // field, begin index, end index + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 0, 1}, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 3, 4}, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 6, 7}, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 9, 10}, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 12, 13}, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 15, 16}, + {UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD, 22, 23}, + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD, 1, 3}, + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD, 4, 6}, + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD, 7, 9}, + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD, 10, 12}, + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD, 13, 15}, + {UFIELD_CATEGORY_LIST, ULISTFMT_LITERAL_FIELD, 16, 22}}; + checkMixedFormattedValue( + message, + ulistfmt_resultAsValue(fl, &ec), + expectedString, + expectedFieldPositions, + UPRV_LENGTHOF(expectedFieldPositions)); + } + + ulistfmt_close(fmt); + ulistfmt_closeResult(fl); +} + #endif /* #if !UCONFIG_NO_FORMATTING */