ICU-13754 Add a ListFormatter FieldPositionIterator format method (#109)

ICU-13754 Reapply PR#106 after sffc rewinding master it.
This commit is contained in:
Frank Tang 2018-09-09 19:14:16 -07:00 committed by Shane Carr
parent fcb82cb744
commit 00ccb44a30
No known key found for this signature in database
GPG Key ID: FCED3B24AAB18B5C
5 changed files with 546 additions and 56 deletions

View File

@ -16,11 +16,16 @@
* created by: Umesh P. Nair * created by: Umesh P. Nair
*/ */
#include "cmemory.h"
#include "unicode/fpositer.h" // FieldPositionIterator
#include "unicode/listformatter.h" #include "unicode/listformatter.h"
#include "unicode/simpleformatter.h" #include "unicode/simpleformatter.h"
#include "unicode/ulistformatter.h"
#include "fphdlimp.h"
#include "mutex.h" #include "mutex.h"
#include "hash.h" #include "hash.h"
#include "cstring.h" #include "cstring.h"
#include "uarrsort.h"
#include "ulocimp.h" #include "ulocimp.h"
#include "charstr.h" #include "charstr.h"
#include "ucln_in.h" #include "ucln_in.h"
@ -61,14 +66,14 @@ ListFormatInternal(const ListFormatInternal &other) :
static Hashtable* listPatternHash = NULL; static Hashtable* listPatternHash = nullptr;
static UMutex listFormatterMutex = U_MUTEX_INITIALIZER; static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
static const char STANDARD_STYLE[] = "standard"; static const char STANDARD_STYLE[] = "standard";
U_CDECL_BEGIN U_CDECL_BEGIN
static UBool U_CALLCONV uprv_listformatter_cleanup() { static UBool U_CALLCONV uprv_listformatter_cleanup() {
delete listPatternHash; delete listPatternHash;
listPatternHash = NULL; listPatternHash = nullptr;
return TRUE; return TRUE;
} }
@ -81,7 +86,7 @@ U_CDECL_END
ListFormatter::ListFormatter(const ListFormatter& other) : ListFormatter::ListFormatter(const ListFormatter& other) :
owned(other.owned), data(other.data) { owned(other.owned), data(other.data) {
if (other.owned != NULL) { if (other.owned != nullptr) {
owned = new ListFormatInternal(*other.owned); owned = new ListFormatInternal(*other.owned);
data = owned; data = owned;
} }
@ -96,7 +101,7 @@ ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
owned = new ListFormatInternal(*other.owned); owned = new ListFormatInternal(*other.owned);
data = owned; data = owned;
} else { } else {
owned = NULL; owned = nullptr;
data = other.data; data = other.data;
} }
return *this; return *this;
@ -108,7 +113,7 @@ void ListFormatter::initializeHash(UErrorCode& errorCode) {
} }
listPatternHash = new Hashtable(); listPatternHash = new Hashtable();
if (listPatternHash == NULL) { if (listPatternHash == nullptr) {
errorCode = U_MEMORY_ALLOCATION_ERROR; errorCode = U_MEMORY_ALLOCATION_ERROR;
return; return;
} }
@ -121,40 +126,40 @@ void ListFormatter::initializeHash(UErrorCode& errorCode) {
const ListFormatInternal* ListFormatter::getListFormatInternal( const ListFormatInternal* ListFormatter::getListFormatInternal(
const Locale& locale, const char *style, UErrorCode& errorCode) { const Locale& locale, const char *style, UErrorCode& errorCode) {
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
return NULL; return nullptr;
} }
CharString keyBuffer(locale.getName(), errorCode); CharString keyBuffer(locale.getName(), errorCode);
keyBuffer.append(':', errorCode).append(style, errorCode); keyBuffer.append(':', errorCode).append(style, errorCode);
UnicodeString key(keyBuffer.data(), -1, US_INV); UnicodeString key(keyBuffer.data(), -1, US_INV);
ListFormatInternal* result = NULL; ListFormatInternal* result = nullptr;
{ {
Mutex m(&listFormatterMutex); Mutex m(&listFormatterMutex);
if (listPatternHash == NULL) { if (listPatternHash == nullptr) {
initializeHash(errorCode); initializeHash(errorCode);
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
return NULL; return nullptr;
} }
} }
result = static_cast<ListFormatInternal*>(listPatternHash->get(key)); result = static_cast<ListFormatInternal*>(listPatternHash->get(key));
} }
if (result != NULL) { if (result != nullptr) {
return result; return result;
} }
result = loadListFormatInternal(locale, style, errorCode); result = loadListFormatInternal(locale, style, errorCode);
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
return NULL; return nullptr;
} }
{ {
Mutex m(&listFormatterMutex); Mutex m(&listFormatterMutex);
ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key)); ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key));
if (temp != NULL) { if (temp != nullptr) {
delete result; delete result;
result = temp; result = temp;
} else { } else {
listPatternHash->put(key, result, errorCode); listPatternHash->put(key, result, errorCode);
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
return NULL; return nullptr;
} }
} }
} }
@ -235,11 +240,11 @@ ListFormatter::ListPatternsSink::~ListPatternsSink() {}
ListFormatInternal* ListFormatter::loadListFormatInternal( ListFormatInternal* ListFormatter::loadListFormatInternal(
const Locale& locale, const char * style, UErrorCode& errorCode) { const Locale& locale, const char * style, UErrorCode& errorCode) {
UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode); UResourceBundle* rb = ures_open(nullptr, locale.getName(), &errorCode);
rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode); rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
ures_close(rb); ures_close(rb);
return NULL; return nullptr;
} }
ListFormatter::ListPatternsSink sink; ListFormatter::ListPatternsSink sink;
char currentStyle[kStyleLenMax+1]; char currentStyle[kStyleLenMax+1];
@ -255,20 +260,20 @@ ListFormatInternal* ListFormatter::loadListFormatInternal(
} }
ures_close(rb); ures_close(rb);
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
return NULL; return nullptr;
} }
if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) { if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) {
errorCode = U_MISSING_RESOURCE_ERROR; errorCode = U_MISSING_RESOURCE_ERROR;
return NULL; return nullptr;
} }
ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, errorCode); ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, errorCode);
if (result == NULL) { if (result == nullptr) {
errorCode = U_MEMORY_ALLOCATION_ERROR; errorCode = U_MEMORY_ALLOCATION_ERROR;
return NULL; return nullptr;
} }
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
delete result; delete result;
return NULL; return nullptr;
} }
return result; return result;
} }
@ -286,12 +291,12 @@ ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *s
Locale tempLocale = locale; Locale tempLocale = locale;
const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode); const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode);
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
return NULL; return nullptr;
} }
ListFormatter* p = new ListFormatter(listFormatInternal); ListFormatter* p = new ListFormatter(listFormatInternal);
if (p == NULL) { if (p == nullptr) {
errorCode = U_MEMORY_ALLOCATION_ERROR; errorCode = U_MEMORY_ALLOCATION_ERROR;
return NULL; return nullptr;
} }
return p; return p;
} }
@ -301,7 +306,7 @@ ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &e
data = owned; data = owned;
} }
ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) { ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(nullptr), data(listFormatterInternal) {
} }
ListFormatter::~ListFormatter() { ListFormatter::~ListFormatter() {
@ -323,6 +328,8 @@ static void joinStringsAndReplace(
UnicodeString &result, UnicodeString &result,
UBool recordOffset, UBool recordOffset,
int32_t &offset, int32_t &offset,
int32_t *offsetFirst,
int32_t *offsetSecond,
UErrorCode& errorCode) { UErrorCode& errorCode) {
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
return; return;
@ -348,6 +355,8 @@ static void joinStringsAndReplace(
} else if (offset >= 0) { } else if (offset >= 0) {
offset += offsets[0]; offset += offsets[0];
} }
if (offsetFirst != nullptr) *offsetFirst = offsets[0];
if (offsetSecond != nullptr) *offsetSecond = offsets[1];
} }
UnicodeString& ListFormatter::format( UnicodeString& ListFormatter::format(
@ -359,6 +368,17 @@ UnicodeString& ListFormatter::format(
return format(items, nItems, appendTo, -1, offset, errorCode); return format(items, nItems, appendTo, -1, offset, errorCode);
} }
UnicodeString& ListFormatter::format(
const UnicodeString items[],
int32_t nItems,
UnicodeString & appendTo,
FieldPositionIterator* posIter,
UErrorCode& errorCode) const {
int32_t offset;
FieldPositionIteratorHandler handler(posIter, errorCode);
return format_(items, nItems, appendTo, -1, offset, &handler, errorCode);
};
UnicodeString& ListFormatter::format( UnicodeString& ListFormatter::format(
const UnicodeString items[], const UnicodeString items[],
int32_t nItems, int32_t nItems,
@ -366,11 +386,22 @@ UnicodeString& ListFormatter::format(
int32_t index, int32_t index,
int32_t &offset, int32_t &offset,
UErrorCode& errorCode) const { UErrorCode& errorCode) const {
return format_(items, nItems, appendTo, index, offset, nullptr, errorCode);
}
UnicodeString& ListFormatter::format_(
const UnicodeString items[],
int32_t nItems,
UnicodeString& appendTo,
int32_t index,
int32_t &offset,
FieldPositionHandler* handler,
UErrorCode& errorCode) const {
offset = -1; offset = -1;
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
return appendTo; return appendTo;
} }
if (data == NULL) { if (data == nullptr) {
errorCode = U_INVALID_STATE_ERROR; errorCode = U_INVALID_STATE_ERROR;
return appendTo; return appendTo;
} }
@ -382,6 +413,11 @@ UnicodeString& ListFormatter::format(
if (index == 0) { if (index == 0) {
offset = appendTo.length(); offset = appendTo.length();
} }
if (handler != nullptr) {
handler->addAttribute(ULISTFMT_ELEMENT_FIELD,
appendTo.length(),
appendTo.length() + items[0].length());
}
appendTo.append(items[0]); appendTo.append(items[0]);
return appendTo; return appendTo;
} }
@ -389,6 +425,12 @@ UnicodeString& ListFormatter::format(
if (index == 0) { if (index == 0) {
offset = 0; offset = 0;
} }
int32_t offsetFirst;
int32_t offsetSecond;
int32_t prefixLength = 0;
// for n items, there are 2 * (n + 1) boundary including 0 and the upper
// edge.
MaybeStackArray<int32_t, 10> offsets((handler != nullptr) ? 2 * (nItems + 1): 0);
joinStringsAndReplace( joinStringsAndReplace(
nItems == 2 ? data->twoPattern : data->startPattern, nItems == 2 ? data->twoPattern : data->startPattern,
result, result,
@ -396,7 +438,14 @@ UnicodeString& ListFormatter::format(
result, result,
index == 1, index == 1,
offset, offset,
&offsetFirst,
&offsetSecond,
errorCode); errorCode);
if (handler != nullptr) {
offsets[0] = 0;
prefixLength += offsetFirst;
offsets[1] = offsetSecond - prefixLength;
}
if (nItems > 2) { if (nItems > 2) {
for (int32_t i = 2; i < nItems - 1; ++i) { for (int32_t i = 2; i < nItems - 1; ++i) {
joinStringsAndReplace( joinStringsAndReplace(
@ -406,7 +455,13 @@ UnicodeString& ListFormatter::format(
result, result,
index == i, index == i,
offset, offset,
&offsetFirst,
&offsetSecond,
errorCode); errorCode);
if (handler != nullptr) {
prefixLength += offsetFirst;
offsets[i] = offsetSecond - prefixLength;
}
} }
joinStringsAndReplace( joinStringsAndReplace(
data->endPattern, data->endPattern,
@ -415,7 +470,45 @@ UnicodeString& ListFormatter::format(
result, result,
index == nItems - 1, index == nItems - 1,
offset, offset,
&offsetFirst,
&offsetSecond,
errorCode); errorCode);
if (handler != nullptr) {
prefixLength += offsetFirst;
offsets[nItems - 1] = offsetSecond - prefixLength;
}
}
if (handler != nullptr) {
// If there are already some data in appendTo, we need to adjust the index
// by shifting that lenght while insert into handler.
int32_t shift = appendTo.length() + prefixLength;
// Output the ULISTFMT_ELEMENT_FIELD in the order of the input elements
for (int32_t i = 0; i < nItems; ++i) {
offsets[i + nItems] = offsets[i] + items[i].length() + shift;
offsets[i] += shift;
handler->addAttribute(
ULISTFMT_ELEMENT_FIELD, // id
offsets[i], // index
offsets[i + nItems]); // limit
}
// The locale pattern may reorder the items (such as in ur-IN locale),
// so we cannot assume the array is in accendning order.
// To handle the edging case, just insert the two ends into the array
// and sort. Then we output ULISTFMT_LITERAL_FIELD if the indecies
// between the even and odd position are not the same in the sorted array.
offsets[2 * nItems] = shift - prefixLength;
offsets[2 * nItems + 1] = result.length() + shift - prefixLength;
uprv_sortArray(offsets.getAlias(), 2 * (nItems + 1), sizeof(int32_t),
uprv_int32Comparator, nullptr,
false, &errorCode);
for (int32_t i = 0; i <= nItems; ++i) {
if (offsets[i * 2] != offsets[i * 2 + 1]) {
handler->addAttribute(
ULISTFMT_LITERAL_FIELD, // id
offsets[i * 2], // index
offsets[i * 2 + 1]); // limit
}
}
} }
if (U_SUCCESS(errorCode)) { if (U_SUCCESS(errorCode)) {
if (offset >= 0) { if (offset >= 0) {

View File

@ -26,6 +26,9 @@
U_NAMESPACE_BEGIN U_NAMESPACE_BEGIN
class FieldPositionIterator;
class FieldPositionHandler;
/** @internal */ /** @internal */
class Hashtable; class Hashtable;
@ -137,6 +140,27 @@ class U_I18N_API ListFormatter : public UObject{
UnicodeString& format(const UnicodeString items[], int32_t n_items, UnicodeString& format(const UnicodeString items[], int32_t n_items,
UnicodeString& appendTo, UErrorCode& errorCode) const; UnicodeString& appendTo, UErrorCode& errorCode) const;
#ifndef U_HIDE_DRAFT_API
/**
* Format a list of strings.
*
* @param items An array of strings to be combined and formatted.
* @param n_items Length of the array items.
* @param appendTo The string to which the formatted result will be
* appended.
* @param posIter On return, can be used to iterate over positions of
* fields generated by this format call. Field values are
* defined in UListFormatterField. Can be NULL.
* @param status ICU error code returned here.
* @return Formatted string combining the elements of items,
* appended to appendTo.
* @draft ICU 63
*/
UnicodeString& format(const UnicodeString items[], int32_t n_items,
UnicodeString & appendTo, FieldPositionIterator* posIter,
UErrorCode& errorCode) const;
#endif /* U_HIDE_DRAFT_API */
#ifndef U_HIDE_INTERNAL_API #ifndef U_HIDE_INTERNAL_API
/** /**
@internal for MeasureFormat @internal for MeasureFormat
@ -164,6 +188,10 @@ class U_I18N_API ListFormatter : public UObject{
struct ListPatternsSink; struct ListPatternsSink;
static ListFormatInternal* loadListFormatInternal(const Locale& locale, const char* style, UErrorCode& errorCode); static ListFormatInternal* loadListFormatInternal(const Locale& locale, const char* style, UErrorCode& errorCode);
UnicodeString& format_(
const UnicodeString items[], int32_t n_items, UnicodeString& appendTo,
int32_t index, int32_t &offset, FieldPositionHandler* handler, UErrorCode& errorCode) const;
ListFormatter(); ListFormatter();
ListFormatInternal* owned; ListFormatInternal* owned;

View File

@ -33,6 +33,26 @@
struct UListFormatter; struct UListFormatter;
typedef struct UListFormatter UListFormatter; /**< C typedef for struct UListFormatter. @stable ICU 55 */ typedef struct UListFormatter UListFormatter; /**< C typedef for struct UListFormatter. @stable ICU 55 */
#ifndef U_HIDE_DRAFT_API
/**
* FieldPosition and UFieldPosition selectors for format fields
* defined by ListFormatter.
* @draft ICU 63
*/
typedef enum UListFormatterField {
/**
* The literal text in the result which came from the resources.
* @draft ICU 63
*/
ULISTFMT_LITERAL_FIELD,
/**
* The element text in the result which came from the input strings.
* @draft ICU 63
*/
ULISTFMT_ELEMENT_FIELD
} UListFormatterField;
#endif // U_HIDE_DRAFT_API
/** /**
* Open a new UListFormatter object using the rules for a given locale. * Open a new UListFormatter object using the rules for a given locale.
* @param locale * @param locale

View File

@ -17,8 +17,61 @@
*/ */
#include "listformattertest.h" #include "listformattertest.h"
#include "unicode/ulistformatter.h"
#include <string.h> #include <string.h>
namespace {
const char* attrString(int32_t attrId) {
switch (attrId) {
case ULISTFMT_LITERAL_FIELD: return "literal";
case ULISTFMT_ELEMENT_FIELD: return "element";
default: return "xxx";
}
}
} // namespace
void ListFormatterTest::ExpectPositions(FieldPositionIterator& iter,
int32_t *values, int32_t tupleCount) {
UBool found[10];
FieldPosition fp;
if (tupleCount > 10) {
assertTrue("internal error, tupleCount too large", FALSE);
} else {
for (int i = 0; i < tupleCount; ++i) {
found[i] = FALSE;
}
}
while (iter.next(fp)) {
UBool ok = FALSE;
int32_t id = fp.getField();
int32_t start = fp.getBeginIndex();
int32_t limit = fp.getEndIndex();
char buf[128];
sprintf(buf, "%24s %3d %3d %3d", attrString(id), id, start, limit);
logln(buf);
for (int i = 0; i < tupleCount; ++i) {
if (found[i]) {
continue;
}
if (values[i*3] == id && values[i*3+1] == start && values[i*3+2] == limit) {
found[i] = ok = TRUE;
break;
}
}
assertTrue((UnicodeString)"found [" + attrString(id) + "," + start + "," + limit + "]", ok);
}
// check that all were found
UBool ok = TRUE;
for (int i = 0; i < tupleCount; ++i) {
if (!found[i]) {
ok = FALSE;
assertTrue((UnicodeString) "missing [" + attrString(values[i*3]) + "," + values[i*3+1] +
"," + values[i*3+2] + "]", found[i]);
}
}
assertTrue("no expected values were missing", ok);
}
ListFormatterTest::ListFormatterTest() : ListFormatterTest::ListFormatterTest() :
prefix("Prefix: ", -1, US_INV), prefix("Prefix: ", -1, US_INV),
one("Alice", -1, US_INV), two("Bob", -1, US_INV), one("Alice", -1, US_INV), two("Bob", -1, US_INV),
@ -26,9 +79,9 @@ ListFormatterTest::ListFormatterTest() :
} }
void ListFormatterTest::CheckFormatting(const ListFormatter* formatter, UnicodeString data[], int32_t dataSize, void ListFormatterTest::CheckFormatting(const ListFormatter* formatter, UnicodeString data[], int32_t dataSize,
const UnicodeString& expected_result) { const UnicodeString& expected_result, const char* testName) {
UnicodeString actualResult(prefix); UnicodeString actualResult(prefix);
UErrorCode errorCode = U_ZERO_ERROR; IcuTestErrorCode errorCode(*this, testName);
formatter->format(data, dataSize, actualResult, errorCode); formatter->format(data, dataSize, actualResult, errorCode);
UnicodeString expectedStringWithPrefix = prefix + expected_result; UnicodeString expectedStringWithPrefix = prefix + expected_result;
if (expectedStringWithPrefix != actualResult) { if (expectedStringWithPrefix != actualResult) {
@ -37,29 +90,29 @@ void ListFormatterTest::CheckFormatting(const ListFormatter* formatter, UnicodeS
} }
void ListFormatterTest::CheckFourCases(const char* locale_string, UnicodeString one, UnicodeString two, void ListFormatterTest::CheckFourCases(const char* locale_string, UnicodeString one, UnicodeString two,
UnicodeString three, UnicodeString four, UnicodeString results[4]) { UnicodeString three, UnicodeString four, UnicodeString results[4], const char* testName) {
UErrorCode errorCode = U_ZERO_ERROR; IcuTestErrorCode errorCode(*this, testName);
LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(Locale(locale_string), errorCode)); LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(Locale(locale_string), errorCode));
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
dataerrln("ListFormatter::createInstance(Locale(\"%s\"), errorCode) failed in CheckFourCases: %s", locale_string, u_errorName(errorCode)); dataerrln("ListFormatter::createInstance(Locale(\"%s\"), errorCode) failed in CheckFourCases: %s", locale_string, u_errorName(errorCode));
return; return;
} }
UnicodeString input1[] = {one}; UnicodeString input1[] = {one};
CheckFormatting(formatter.getAlias(), input1, 1, results[0]); CheckFormatting(formatter.getAlias(), input1, 1, results[0], testName);
UnicodeString input2[] = {one, two}; UnicodeString input2[] = {one, two};
CheckFormatting(formatter.getAlias(), input2, 2, results[1]); CheckFormatting(formatter.getAlias(), input2, 2, results[1], testName);
UnicodeString input3[] = {one, two, three}; UnicodeString input3[] = {one, two, three};
CheckFormatting(formatter.getAlias(), input3, 3, results[2]); CheckFormatting(formatter.getAlias(), input3, 3, results[2], testName);
UnicodeString input4[] = {one, two, three, four}; UnicodeString input4[] = {one, two, three, four};
CheckFormatting(formatter.getAlias(), input4, 4, results[3]); CheckFormatting(formatter.getAlias(), input4, 4, results[3], testName);
} }
UBool ListFormatterTest::RecordFourCases(const Locale& locale, UnicodeString one, UnicodeString two, UBool ListFormatterTest::RecordFourCases(const Locale& locale, UnicodeString one, UnicodeString two,
UnicodeString three, UnicodeString four, UnicodeString results[4]) { UnicodeString three, UnicodeString four, UnicodeString results[4], const char* testName) {
UErrorCode errorCode = U_ZERO_ERROR; IcuTestErrorCode errorCode(*this, testName);
LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(locale, errorCode)); LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(locale, errorCode));
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
dataerrln("ListFormatter::createInstance(\"%s\", errorCode) failed in RecordFourCases: %s", locale.getName(), u_errorName(errorCode)); dataerrln("ListFormatter::createInstance(\"%s\", errorCode) failed in RecordFourCases: %s", locale.getName(), u_errorName(errorCode));
@ -88,14 +141,14 @@ void ListFormatterTest::TestRoot() {
one + ", " + two + ", " + three + ", " + four one + ", " + two + ", " + three + ", " + four
}; };
CheckFourCases("", one, two, three, four, results); CheckFourCases("", one, two, three, four, results, "TestRoot()");
} }
// Bogus locale should fallback to root. // Bogus locale should fallback to root.
void ListFormatterTest::TestBogus() { void ListFormatterTest::TestBogus() {
UnicodeString results[4]; UnicodeString results[4];
if (RecordFourCases(Locale::getDefault(), one, two, three, four, results)) { if (RecordFourCases(Locale::getDefault(), one, two, three, four, results, "TestBogus()")) {
CheckFourCases("ex_PY", one, two, three, four, results); CheckFourCases("ex_PY", one, two, three, four, results, "TestBogus()");
} }
} }
@ -109,11 +162,11 @@ void ListFormatterTest::TestEnglish() {
one + ", " + two + ", " + three + ", and " + four one + ", " + two + ", " + three + ", and " + four
}; };
CheckFourCases("en", one, two, three, four, results); CheckFourCases("en", one, two, three, four, results, "TestEnglish()");
} }
void ListFormatterTest::Test9946() { void ListFormatterTest::Test9946() {
UErrorCode errorCode = U_ZERO_ERROR; IcuTestErrorCode errorCode(*this, "Test9946()");
LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(Locale("en"), errorCode)); LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(Locale("en"), errorCode));
if (U_FAILURE(errorCode)) { if (U_FAILURE(errorCode)) {
dataerrln( dataerrln(
@ -144,7 +197,7 @@ void ListFormatterTest::TestEnglishUS() {
one + ", " + two + ", " + three + ", and " + four one + ", " + two + ", " + three + ", and " + four
}; };
CheckFourCases("en_US", one, two, three, four, results); CheckFourCases("en_US", one, two, three, four, results, "TestEnglishUS()");
} }
// Tests resource loading and inheritance when region sublocale // Tests resource loading and inheritance when region sublocale
@ -158,7 +211,231 @@ void ListFormatterTest::TestEnglishGB() {
one + ", " + two + ", " + three + " and " + four one + ", " + two + ", " + three + " and " + four
}; };
CheckFourCases("en_GB", one, two, three, four, results); CheckFourCases("en_GB", one, two, three, four, results, "TestEnglishGB()");
}
void ListFormatterTest::TestFieldPositionIteratorWontCrash() {
IcuTestErrorCode errorCode(*this, "TestFieldPositionIteratorWontCrash()");
LocalPointer<ListFormatter> formatter(
ListFormatter::createInstance(Locale("en"), errorCode));
if (U_FAILURE(errorCode)) {
dataerrln(
"ListFormatter::createInstance(Locale(\"en\"), errorCode) failed in "
"TestFieldPositionIteratorWontCrash: %s",
u_errorName(errorCode));
return;
}
UnicodeString data[3] = {"a", "bbb", "cc"};
UnicodeString actualResult;
// make sure NULL as FieldPositionIterator won't caused crash.
formatter->format(data, 3, actualResult, nullptr, errorCode);
if (U_FAILURE(errorCode)) {
dataerrln(
"ListFormatter::format(data, 3, nullptr, errorCode) "
"failed in TestFieldPositionIteratorWontCrash: %s",
u_errorName(errorCode));
return;
}
}
void ListFormatterTest::RunTestFieldPositionIteratorWithFormatter(
ListFormatter* formatter,
UnicodeString data[], int32_t n, int32_t expected[], int32_t tupleCount,
UnicodeString& appendTo, const char16_t *expectedFormatted,
const char* testName) {
IcuTestErrorCode errorCode(*this, testName);
FieldPositionIterator iter;
formatter->format(data, n, appendTo, &iter, errorCode);
if (U_FAILURE(errorCode)) {
dataerrln(
"ListFormatter::format(data, %d, &iter, errorCode) "
"failed in %s: %s", n, testName, u_errorName(errorCode));
return;
}
if (appendTo != expectedFormatted) {
errln(UnicodeString("Expected: |") + expectedFormatted + "|, Actual: |" + appendTo + "|");
}
ExpectPositions(iter, expected, tupleCount);
}
void ListFormatterTest::RunTestFieldPositionIteratorWithNItemsPatternShift(
UnicodeString data[], int32_t n, int32_t expected[], int32_t tupleCount,
UnicodeString& appendTo, const char16_t *expectedFormatted,
const char* testName) {
IcuTestErrorCode errorCode(*this, testName);
LocalPointer<ListFormatter> formatter(
ListFormatter::createInstance(Locale("ur", "IN"), "unit-narrow", errorCode));
if (U_FAILURE(errorCode)) {
dataerrln(
"ListFormatter::createInstance(Locale(\"ur\", \"IN\"), \"unit-narrow\", errorCode) failed in "
"%s: %s", testName, u_errorName(errorCode));
return;
}
RunTestFieldPositionIteratorWithFormatter(
formatter.getAlias(),
data, n, expected, tupleCount, appendTo, expectedFormatted, testName);
}
void ListFormatterTest::RunTestFieldPositionIteratorWithNItems(
UnicodeString data[], int32_t n, int32_t expected[], int32_t tupleCount,
UnicodeString& appendTo, const char16_t *expectedFormatted,
const char* testName) {
IcuTestErrorCode errorCode(*this, testName);
LocalPointer<ListFormatter> formatter(
ListFormatter::createInstance(Locale("en"), errorCode));
if (U_FAILURE(errorCode)) {
dataerrln(
"ListFormatter::createInstance(Locale(\"en\"), errorCode) failed in "
"%s: %s", testName, u_errorName(errorCode));
return;
}
RunTestFieldPositionIteratorWithFormatter(
formatter.getAlias(),
data, n, expected, tupleCount, appendTo, expectedFormatted, testName);
}
void ListFormatterTest::TestFieldPositionIteratorWith3ItemsAndDataBefore() {
// 0 1 2
// 0123456789012345678901234567
// "Hello World: a, bbb, and cc"
UnicodeString data[3] = {"a", "bbb", "cc"};
int32_t expected[] = {
ULISTFMT_ELEMENT_FIELD, 13, 14,
ULISTFMT_LITERAL_FIELD, 14, 16,
ULISTFMT_ELEMENT_FIELD, 16, 19,
ULISTFMT_LITERAL_FIELD, 19, 25,
ULISTFMT_ELEMENT_FIELD, 25, 27
};
int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
UnicodeString appendTo(u"Hello World: ");
RunTestFieldPositionIteratorWithNItems(
data, 3, expected, tupleCount, appendTo,
u"Hello World: a, bbb, and cc",
"TestFieldPositionIteratorWith3ItemsAndDataBefore");
}
void ListFormatterTest::TestFieldPositionIteratorWith3Items() {
// 0 1
// 012345678901234
// "a, bbb, and cc"
UnicodeString data[3] = {"a", "bbb", "cc"};
int32_t expected[] = {
ULISTFMT_ELEMENT_FIELD, 0, 1,
ULISTFMT_LITERAL_FIELD, 1, 3,
ULISTFMT_ELEMENT_FIELD, 3, 6,
ULISTFMT_LITERAL_FIELD, 6, 12,
ULISTFMT_ELEMENT_FIELD, 12, 14
};
int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
UnicodeString appendTo;
RunTestFieldPositionIteratorWithNItems(
data, 3, expected, tupleCount, appendTo,
u"a, bbb, and cc",
"TestFieldPositionIteratorWith3Items");
}
void ListFormatterTest::TestFieldPositionIteratorWith3ItemsPatternShift() {
// 0 1
// 012345678901234
// "cc bbb a"
UnicodeString data[3] = {"a", "bbb", "cc"};
int32_t expected[] = {
ULISTFMT_ELEMENT_FIELD, 7, 8,
ULISTFMT_LITERAL_FIELD, 6, 7,
ULISTFMT_ELEMENT_FIELD, 3, 6,
ULISTFMT_LITERAL_FIELD, 2, 3,
ULISTFMT_ELEMENT_FIELD, 0, 2
};
int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
UnicodeString appendTo;
RunTestFieldPositionIteratorWithNItemsPatternShift(
data, 3, expected, tupleCount, appendTo,
u"cc bbb a",
"TestFieldPositionIteratorWith3ItemsPatternShift");
}
void ListFormatterTest::TestFieldPositionIteratorWith2ItemsAndDataBefore() {
// 0 1
// 0123456789012345
// "Foo: bbb and cc"
UnicodeString data[2] = {"bbb", "cc"};
int32_t expected[] = {
ULISTFMT_ELEMENT_FIELD, 5, 8,
ULISTFMT_LITERAL_FIELD, 8, 13,
ULISTFMT_ELEMENT_FIELD, 13, 15
};
int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
UnicodeString appendTo("Foo: ");
RunTestFieldPositionIteratorWithNItems(
data, 2, expected, tupleCount, appendTo,
u"Foo: bbb and cc",
"TestFieldPositionIteratorWith2ItemsAndDataBefore");
}
void ListFormatterTest::TestFieldPositionIteratorWith2Items() {
// 0 1
// 01234567890
// "bbb and cc"
UnicodeString data[2] = {"bbb", "cc"};
int32_t expected[] = {
ULISTFMT_ELEMENT_FIELD, 0, 3,
ULISTFMT_LITERAL_FIELD, 3, 8,
ULISTFMT_ELEMENT_FIELD, 8, 10
};
int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
UnicodeString appendTo;
RunTestFieldPositionIteratorWithNItems(
data, 2, expected, tupleCount, appendTo,
u"bbb and cc",
"TestFieldPositionIteratorWith2Items");
}
void ListFormatterTest::TestFieldPositionIteratorWith2ItemsPatternShift() {
// 0 1
// 01234567890
// "cc bbb"
UnicodeString data[2] = {"bbb", "cc"};
int32_t expected[] = {
ULISTFMT_ELEMENT_FIELD, 3, 6,
ULISTFMT_LITERAL_FIELD, 2, 3,
ULISTFMT_ELEMENT_FIELD, 0, 2
};
int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
UnicodeString appendTo;
RunTestFieldPositionIteratorWithNItemsPatternShift(
data, 2, expected, tupleCount, appendTo,
u"cc bbb",
"TestFieldPositionIteratorWith2ItemsPatternShift");
}
void ListFormatterTest::TestFieldPositionIteratorWith1ItemAndDataBefore() {
// 012345678
// "Hello cc"
UnicodeString data[1] = {"cc"};
int32_t expected[] = {
ULISTFMT_ELEMENT_FIELD, 6, 8
};
int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
UnicodeString appendTo("Hello ");
RunTestFieldPositionIteratorWithNItems(
data, 1, expected, tupleCount, appendTo,
u"Hello cc",
"TestFieldPositionIteratorWith1ItemAndDataBefore");
}
void ListFormatterTest::TestFieldPositionIteratorWith1Item() {
// 012
// "cc"
UnicodeString data[1] = {"cc"};
int32_t expected[] = {
ULISTFMT_ELEMENT_FIELD, 0, 2
};
int32_t tupleCount = sizeof(expected)/(3 * sizeof(*expected));
UnicodeString appendTo;
RunTestFieldPositionIteratorWithNItems(
data, 1, expected, tupleCount, appendTo,
u"cc",
"TestFieldPositionIteratorWith1Item");
} }
// Tests resource loading and inheritance when region sublocale // Tests resource loading and inheritance when region sublocale
@ -172,7 +449,7 @@ void ListFormatterTest::TestNynorsk() {
one + ", " + two + ", " + three + " og " + four one + ", " + two + ", " + three + " og " + four
}; };
CheckFourCases("nn", one, two, three, four, results); CheckFourCases("nn", one, two, three, four, results, "TestNynorsk()");
} }
// Tests resource loading and inheritance when region sublocale // Tests resource loading and inheritance when region sublocale
@ -188,7 +465,7 @@ void ListFormatterTest::TestChineseTradHK() {
one + comma_string + two + comma_string + three + and_string + four one + comma_string + two + comma_string + three + and_string + four
}; };
CheckFourCases("zh_Hant_HK", one, two, three, four, results); CheckFourCases("zh_Hant_HK", one, two, three, four, results, "TestChineseTradHK()");
} }
// Formatting in Russian. // Formatting in Russian.
@ -202,7 +479,7 @@ void ListFormatterTest::TestRussian() {
one + ", " + two + ", " + three + and_string + four one + ", " + two + ", " + three + and_string + four
}; };
CheckFourCases("ru", one, two, three, four, results); CheckFourCases("ru", one, two, three, four, results, "TestRussian()");
} }
// Formatting in Malayalam. // Formatting in Malayalam.
@ -219,7 +496,7 @@ void ListFormatterTest::TestMalayalam() {
one + ", " + two + ", " + three + ", " + four + total_string one + ", " + two + ", " + three + ", " + four + total_string
}; };
CheckFourCases("ml", one, two, three, four, results); CheckFourCases("ml", one, two, three, four, results, "TestMalayalam()");
} }
// Formatting in Zulu. // Formatting in Zulu.
@ -232,7 +509,7 @@ void ListFormatterTest::TestZulu() {
one + ", " + two + ", " + three + ", ne-" + four one + ", " + two + ", " + three + ", ne-" + four
}; };
CheckFourCases("zu", one, two, three, four, results); CheckFourCases("zu", one, two, three, four, results, "TestZulu()");
} }
void ListFormatterTest::TestOutOfOrderPatterns() { void ListFormatterTest::TestOutOfOrderPatterns() {
@ -243,22 +520,22 @@ void ListFormatterTest::TestOutOfOrderPatterns() {
four + " in the last after " + three + " after " + two + " after the first " + one four + " in the last after " + three + " after " + two + " after the first " + one
}; };
UErrorCode errorCode = U_ZERO_ERROR; IcuTestErrorCode errorCode(*this, "TestOutOfOrderPatterns()");
ListFormatData data("{1} after {0}", "{1} after the first {0}", ListFormatData data("{1} after {0}", "{1} after the first {0}",
"{1} after {0}", "{1} in the last after {0}"); "{1} after {0}", "{1} in the last after {0}");
ListFormatter formatter(data, errorCode); ListFormatter formatter(data, errorCode);
UnicodeString input1[] = {one}; UnicodeString input1[] = {one};
CheckFormatting(&formatter, input1, 1, results[0]); CheckFormatting(&formatter, input1, 1, results[0], "TestOutOfOrderPatterns()");
UnicodeString input2[] = {one, two}; UnicodeString input2[] = {one, two};
CheckFormatting(&formatter, input2, 2, results[1]); CheckFormatting(&formatter, input2, 2, results[1], "TestOutOfOrderPatterns()");
UnicodeString input3[] = {one, two, three}; UnicodeString input3[] = {one, two, three};
CheckFormatting(&formatter, input3, 3, results[2]); CheckFormatting(&formatter, input3, 3, results[2], "TestOutOfOrderPatterns()");
UnicodeString input4[] = {one, two, three, four}; UnicodeString input4[] = {one, two, three, four};
CheckFormatting(&formatter, input4, 4, results[3]); CheckFormatting(&formatter, input4, 4, results[3], "TestOutOfOrderPatterns()");
} }
void ListFormatterTest::runIndexedTest(int32_t index, UBool exec, void ListFormatterTest::runIndexedTest(int32_t index, UBool exec,
@ -276,7 +553,33 @@ void ListFormatterTest::runIndexedTest(int32_t index, UBool exec,
case 9: name = "TestEnglishGB"; if (exec) TestEnglishGB(); break; case 9: name = "TestEnglishGB"; if (exec) TestEnglishGB(); break;
case 10: name = "TestNynorsk"; if (exec) TestNynorsk(); break; case 10: name = "TestNynorsk"; if (exec) TestNynorsk(); break;
case 11: name = "TestChineseTradHK"; if (exec) TestChineseTradHK(); break; case 11: name = "TestChineseTradHK"; if (exec) TestChineseTradHK(); break;
case 12: name = "TestFieldPositionIteratorWontCrash";
if (exec) TestFieldPositionIteratorWontCrash();
break;
case 13: name = "TestFieldPositionIteratorWith1Item";
if (exec) TestFieldPositionIteratorWith1Item();
break;
case 14: name = "TestFieldPositionIteratorWith1ItemAndDataBefore";
if (exec) TestFieldPositionIteratorWith1ItemAndDataBefore();
break;
case 15: name = "TestFieldPositionIteratorWith2Items";
if (exec) TestFieldPositionIteratorWith2Items();
break;
case 16: name = "TestFieldPositionIteratorWith2ItemsAndDataBefore";
if (exec) TestFieldPositionIteratorWith2ItemsAndDataBefore();
break;
case 17: name = "TestFieldPositionIteratorWith2ItemsPatternShift";
if (exec) TestFieldPositionIteratorWith2ItemsPatternShift();
break;
case 18: name = "TestFieldPositionIteratorWith3Items";
if (exec) TestFieldPositionIteratorWith3Items();
break;
case 19: name = "TestFieldPositionIteratorWith3ItemsAndDataBefore";
if (exec) TestFieldPositionIteratorWith3ItemsAndDataBefore();
break;
case 20: name = "TestFieldPositionIteratorWith3ItemsPatternShift";
if (exec) TestFieldPositionIteratorWith3ItemsPatternShift();
break;
default: name = ""; break; default: name = ""; break;
} }
} }

View File

@ -19,6 +19,7 @@
#ifndef __LISTFORMATTERTEST_H__ #ifndef __LISTFORMATTERTEST_H__
#define __LISTFORMATTERTEST_H__ #define __LISTFORMATTERTEST_H__
#include "unicode/fpositer.h"
#include "unicode/listformatter.h" #include "unicode/listformatter.h"
#include "intltest.h" #include "intltest.h"
@ -41,23 +42,68 @@ class ListFormatterTest : public IntlTest {
void TestZulu(); void TestZulu();
void TestOutOfOrderPatterns(); void TestOutOfOrderPatterns();
void Test9946(); void Test9946();
void TestFieldPositionIteratorWontCrash();
void TestFieldPositionIteratorWith1Item();
void TestFieldPositionIteratorWith2Items();
void TestFieldPositionIteratorWith3Items();
void TestFieldPositionIteratorWith1ItemAndDataBefore();
void TestFieldPositionIteratorWith2ItemsAndDataBefore();
void TestFieldPositionIteratorWith3ItemsAndDataBefore();
void TestFieldPositionIteratorWith2ItemsPatternShift();
void TestFieldPositionIteratorWith3ItemsPatternShift();
private: private:
void CheckFormatting(const ListFormatter* formatter, UnicodeString data[], int32_t data_size, const UnicodeString& expected_result); void CheckFormatting(
const ListFormatter* formatter,
UnicodeString data[],
int32_t data_size,
const UnicodeString& expected_result,
const char* testName);
void ExpectPositions(
FieldPositionIterator& iter,
int32_t *values,
int32_t tupleCount);
void RunTestFieldPositionIteratorWithNItems(
UnicodeString *data,
int32_t n,
int32_t *values,
int32_t tupleCount,
UnicodeString& appendTo,
const char16_t *expectedFormatted,
const char* testName);
void RunTestFieldPositionIteratorWithNItemsPatternShift(
UnicodeString *data,
int32_t n,
int32_t *values,
int32_t tupleCount,
UnicodeString& appendTo,
const char16_t *expectedFormatted,
const char* testName);
void RunTestFieldPositionIteratorWithFormatter(
ListFormatter* formatter,
UnicodeString *data,
int32_t n,
int32_t *values,
int32_t tupleCount,
UnicodeString& appendTo,
const char16_t *expectedFormatted,
const char* testName);
void CheckFourCases( void CheckFourCases(
const char* locale_string, const char* locale_string,
UnicodeString one, UnicodeString one,
UnicodeString two, UnicodeString two,
UnicodeString three, UnicodeString three,
UnicodeString four, UnicodeString four,
UnicodeString results[4]); UnicodeString results[4],
const char* testName);
UBool RecordFourCases( UBool RecordFourCases(
const Locale& locale, const Locale& locale,
UnicodeString one, UnicodeString one,
UnicodeString two, UnicodeString two,
UnicodeString three, UnicodeString three,
UnicodeString four, UnicodeString four,
UnicodeString results[4]); UnicodeString results[4],
const char* testName);
private: private:
// Reused test data. // Reused test data.