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
*/
#include "cmemory.h"
#include "unicode/fpositer.h" // FieldPositionIterator
#include "unicode/listformatter.h"
#include "unicode/simpleformatter.h"
#include "unicode/ulistformatter.h"
#include "fphdlimp.h"
#include "mutex.h"
#include "hash.h"
#include "cstring.h"
#include "uarrsort.h"
#include "ulocimp.h"
#include "charstr.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 const char STANDARD_STYLE[] = "standard";
U_CDECL_BEGIN
static UBool U_CALLCONV uprv_listformatter_cleanup() {
delete listPatternHash;
listPatternHash = NULL;
listPatternHash = nullptr;
return TRUE;
}
@ -81,7 +86,7 @@ U_CDECL_END
ListFormatter::ListFormatter(const ListFormatter& other) :
owned(other.owned), data(other.data) {
if (other.owned != NULL) {
if (other.owned != nullptr) {
owned = new ListFormatInternal(*other.owned);
data = owned;
}
@ -96,7 +101,7 @@ ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
owned = new ListFormatInternal(*other.owned);
data = owned;
} else {
owned = NULL;
owned = nullptr;
data = other.data;
}
return *this;
@ -108,7 +113,7 @@ void ListFormatter::initializeHash(UErrorCode& errorCode) {
}
listPatternHash = new Hashtable();
if (listPatternHash == NULL) {
if (listPatternHash == nullptr) {
errorCode = U_MEMORY_ALLOCATION_ERROR;
return;
}
@ -121,40 +126,40 @@ void ListFormatter::initializeHash(UErrorCode& errorCode) {
const ListFormatInternal* ListFormatter::getListFormatInternal(
const Locale& locale, const char *style, UErrorCode& errorCode) {
if (U_FAILURE(errorCode)) {
return NULL;
return nullptr;
}
CharString keyBuffer(locale.getName(), errorCode);
keyBuffer.append(':', errorCode).append(style, errorCode);
UnicodeString key(keyBuffer.data(), -1, US_INV);
ListFormatInternal* result = NULL;
ListFormatInternal* result = nullptr;
{
Mutex m(&listFormatterMutex);
if (listPatternHash == NULL) {
if (listPatternHash == nullptr) {
initializeHash(errorCode);
if (U_FAILURE(errorCode)) {
return NULL;
return nullptr;
}
}
result = static_cast<ListFormatInternal*>(listPatternHash->get(key));
}
if (result != NULL) {
if (result != nullptr) {
return result;
}
result = loadListFormatInternal(locale, style, errorCode);
if (U_FAILURE(errorCode)) {
return NULL;
return nullptr;
}
{
Mutex m(&listFormatterMutex);
ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key));
if (temp != NULL) {
if (temp != nullptr) {
delete result;
result = temp;
} else {
listPatternHash->put(key, result, errorCode);
if (U_FAILURE(errorCode)) {
return NULL;
return nullptr;
}
}
}
@ -235,11 +240,11 @@ ListFormatter::ListPatternsSink::~ListPatternsSink() {}
ListFormatInternal* ListFormatter::loadListFormatInternal(
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);
if (U_FAILURE(errorCode)) {
ures_close(rb);
return NULL;
return nullptr;
}
ListFormatter::ListPatternsSink sink;
char currentStyle[kStyleLenMax+1];
@ -255,20 +260,20 @@ ListFormatInternal* ListFormatter::loadListFormatInternal(
}
ures_close(rb);
if (U_FAILURE(errorCode)) {
return NULL;
return nullptr;
}
if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) {
errorCode = U_MISSING_RESOURCE_ERROR;
return NULL;
return nullptr;
}
ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, errorCode);
if (result == NULL) {
if (result == nullptr) {
errorCode = U_MEMORY_ALLOCATION_ERROR;
return NULL;
return nullptr;
}
if (U_FAILURE(errorCode)) {
delete result;
return NULL;
return nullptr;
}
return result;
}
@ -286,12 +291,12 @@ ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *s
Locale tempLocale = locale;
const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode);
if (U_FAILURE(errorCode)) {
return NULL;
return nullptr;
}
ListFormatter* p = new ListFormatter(listFormatInternal);
if (p == NULL) {
if (p == nullptr) {
errorCode = U_MEMORY_ALLOCATION_ERROR;
return NULL;
return nullptr;
}
return p;
}
@ -301,7 +306,7 @@ ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &e
data = owned;
}
ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) {
ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(nullptr), data(listFormatterInternal) {
}
ListFormatter::~ListFormatter() {
@ -323,6 +328,8 @@ static void joinStringsAndReplace(
UnicodeString &result,
UBool recordOffset,
int32_t &offset,
int32_t *offsetFirst,
int32_t *offsetSecond,
UErrorCode& errorCode) {
if (U_FAILURE(errorCode)) {
return;
@ -348,6 +355,8 @@ static void joinStringsAndReplace(
} else if (offset >= 0) {
offset += offsets[0];
}
if (offsetFirst != nullptr) *offsetFirst = offsets[0];
if (offsetSecond != nullptr) *offsetSecond = offsets[1];
}
UnicodeString& ListFormatter::format(
@ -359,6 +368,17 @@ UnicodeString& ListFormatter::format(
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(
const UnicodeString items[],
int32_t nItems,
@ -366,11 +386,22 @@ UnicodeString& ListFormatter::format(
int32_t index,
int32_t &offset,
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;
if (U_FAILURE(errorCode)) {
return appendTo;
}
if (data == NULL) {
if (data == nullptr) {
errorCode = U_INVALID_STATE_ERROR;
return appendTo;
}
@ -382,6 +413,11 @@ UnicodeString& ListFormatter::format(
if (index == 0) {
offset = appendTo.length();
}
if (handler != nullptr) {
handler->addAttribute(ULISTFMT_ELEMENT_FIELD,
appendTo.length(),
appendTo.length() + items[0].length());
}
appendTo.append(items[0]);
return appendTo;
}
@ -389,6 +425,12 @@ UnicodeString& ListFormatter::format(
if (index == 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(
nItems == 2 ? data->twoPattern : data->startPattern,
result,
@ -396,7 +438,14 @@ UnicodeString& ListFormatter::format(
result,
index == 1,
offset,
&offsetFirst,
&offsetSecond,
errorCode);
if (handler != nullptr) {
offsets[0] = 0;
prefixLength += offsetFirst;
offsets[1] = offsetSecond - prefixLength;
}
if (nItems > 2) {
for (int32_t i = 2; i < nItems - 1; ++i) {
joinStringsAndReplace(
@ -406,7 +455,13 @@ UnicodeString& ListFormatter::format(
result,
index == i,
offset,
&offsetFirst,
&offsetSecond,
errorCode);
if (handler != nullptr) {
prefixLength += offsetFirst;
offsets[i] = offsetSecond - prefixLength;
}
}
joinStringsAndReplace(
data->endPattern,
@ -415,7 +470,45 @@ UnicodeString& ListFormatter::format(
result,
index == nItems - 1,
offset,
&offsetFirst,
&offsetSecond,
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 (offset >= 0) {

View File

@ -26,6 +26,9 @@
U_NAMESPACE_BEGIN
class FieldPositionIterator;
class FieldPositionHandler;
/** @internal */
class Hashtable;
@ -137,6 +140,27 @@ class U_I18N_API ListFormatter : public UObject{
UnicodeString& format(const UnicodeString items[], int32_t n_items,
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
/**
@internal for MeasureFormat
@ -164,6 +188,10 @@ class U_I18N_API ListFormatter : public UObject{
struct ListPatternsSink;
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();
ListFormatInternal* owned;

View File

@ -33,6 +33,26 @@
struct UListFormatter;
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.
* @param locale

View File

@ -17,8 +17,61 @@
*/
#include "listformattertest.h"
#include "unicode/ulistformatter.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() :
prefix("Prefix: ", -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,
const UnicodeString& expected_result) {
const UnicodeString& expected_result, const char* testName) {
UnicodeString actualResult(prefix);
UErrorCode errorCode = U_ZERO_ERROR;
IcuTestErrorCode errorCode(*this, testName);
formatter->format(data, dataSize, actualResult, errorCode);
UnicodeString expectedStringWithPrefix = prefix + expected_result;
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,
UnicodeString three, UnicodeString four, UnicodeString results[4]) {
UErrorCode errorCode = U_ZERO_ERROR;
UnicodeString three, UnicodeString four, UnicodeString results[4], const char* testName) {
IcuTestErrorCode errorCode(*this, testName);
LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(Locale(locale_string), errorCode));
if (U_FAILURE(errorCode)) {
dataerrln("ListFormatter::createInstance(Locale(\"%s\"), errorCode) failed in CheckFourCases: %s", locale_string, u_errorName(errorCode));
return;
}
UnicodeString input1[] = {one};
CheckFormatting(formatter.getAlias(), input1, 1, results[0]);
CheckFormatting(formatter.getAlias(), input1, 1, results[0], testName);
UnicodeString input2[] = {one, two};
CheckFormatting(formatter.getAlias(), input2, 2, results[1]);
CheckFormatting(formatter.getAlias(), input2, 2, results[1], testName);
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};
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,
UnicodeString three, UnicodeString four, UnicodeString results[4]) {
UErrorCode errorCode = U_ZERO_ERROR;
UnicodeString three, UnicodeString four, UnicodeString results[4], const char* testName) {
IcuTestErrorCode errorCode(*this, testName);
LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(locale, errorCode));
if (U_FAILURE(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
};
CheckFourCases("", one, two, three, four, results);
CheckFourCases("", one, two, three, four, results, "TestRoot()");
}
// Bogus locale should fallback to root.
void ListFormatterTest::TestBogus() {
UnicodeString results[4];
if (RecordFourCases(Locale::getDefault(), one, two, three, four, results)) {
CheckFourCases("ex_PY", one, two, three, four, results);
if (RecordFourCases(Locale::getDefault(), one, two, three, four, results, "TestBogus()")) {
CheckFourCases("ex_PY", one, two, three, four, results, "TestBogus()");
}
}
@ -109,11 +162,11 @@ void ListFormatterTest::TestEnglish() {
one + ", " + two + ", " + three + ", and " + four
};
CheckFourCases("en", one, two, three, four, results);
CheckFourCases("en", one, two, three, four, results, "TestEnglish()");
}
void ListFormatterTest::Test9946() {
UErrorCode errorCode = U_ZERO_ERROR;
IcuTestErrorCode errorCode(*this, "Test9946()");
LocalPointer<ListFormatter> formatter(ListFormatter::createInstance(Locale("en"), errorCode));
if (U_FAILURE(errorCode)) {
dataerrln(
@ -144,7 +197,7 @@ void ListFormatterTest::TestEnglishUS() {
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
@ -158,7 +211,231 @@ void ListFormatterTest::TestEnglishGB() {
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
@ -172,7 +449,7 @@ void ListFormatterTest::TestNynorsk() {
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
@ -188,7 +465,7 @@ void ListFormatterTest::TestChineseTradHK() {
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.
@ -202,7 +479,7 @@ void ListFormatterTest::TestRussian() {
one + ", " + two + ", " + three + and_string + four
};
CheckFourCases("ru", one, two, three, four, results);
CheckFourCases("ru", one, two, three, four, results, "TestRussian()");
}
// Formatting in Malayalam.
@ -219,7 +496,7 @@ void ListFormatterTest::TestMalayalam() {
one + ", " + two + ", " + three + ", " + four + total_string
};
CheckFourCases("ml", one, two, three, four, results);
CheckFourCases("ml", one, two, three, four, results, "TestMalayalam()");
}
// Formatting in Zulu.
@ -232,7 +509,7 @@ void ListFormatterTest::TestZulu() {
one + ", " + two + ", " + three + ", ne-" + four
};
CheckFourCases("zu", one, two, three, four, results);
CheckFourCases("zu", one, two, three, four, results, "TestZulu()");
}
void ListFormatterTest::TestOutOfOrderPatterns() {
@ -243,22 +520,22 @@ void ListFormatterTest::TestOutOfOrderPatterns() {
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}",
"{1} after {0}", "{1} in the last after {0}");
ListFormatter formatter(data, errorCode);
UnicodeString input1[] = {one};
CheckFormatting(&formatter, input1, 1, results[0]);
CheckFormatting(&formatter, input1, 1, results[0], "TestOutOfOrderPatterns()");
UnicodeString input2[] = {one, two};
CheckFormatting(&formatter, input2, 2, results[1]);
CheckFormatting(&formatter, input2, 2, results[1], "TestOutOfOrderPatterns()");
UnicodeString input3[] = {one, two, three};
CheckFormatting(&formatter, input3, 3, results[2]);
CheckFormatting(&formatter, input3, 3, results[2], "TestOutOfOrderPatterns()");
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,
@ -276,7 +553,33 @@ void ListFormatterTest::runIndexedTest(int32_t index, UBool exec,
case 9: name = "TestEnglishGB"; if (exec) TestEnglishGB(); break;
case 10: name = "TestNynorsk"; if (exec) TestNynorsk(); 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;
}
}

View File

@ -19,6 +19,7 @@
#ifndef __LISTFORMATTERTEST_H__
#define __LISTFORMATTERTEST_H__
#include "unicode/fpositer.h"
#include "unicode/listformatter.h"
#include "intltest.h"
@ -41,23 +42,68 @@ class ListFormatterTest : public IntlTest {
void TestZulu();
void TestOutOfOrderPatterns();
void Test9946();
void TestFieldPositionIteratorWontCrash();
void TestFieldPositionIteratorWith1Item();
void TestFieldPositionIteratorWith2Items();
void TestFieldPositionIteratorWith3Items();
void TestFieldPositionIteratorWith1ItemAndDataBefore();
void TestFieldPositionIteratorWith2ItemsAndDataBefore();
void TestFieldPositionIteratorWith3ItemsAndDataBefore();
void TestFieldPositionIteratorWith2ItemsPatternShift();
void TestFieldPositionIteratorWith3ItemsPatternShift();
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(
const char* locale_string,
UnicodeString one,
UnicodeString two,
UnicodeString three,
UnicodeString four,
UnicodeString results[4]);
UnicodeString results[4],
const char* testName);
UBool RecordFourCases(
const Locale& locale,
UnicodeString one,
UnicodeString two,
UnicodeString three,
UnicodeString four,
UnicodeString results[4]);
UnicodeString results[4],
const char* testName);
private:
// Reused test data.