Index: source/common/listformatter.cpp |
diff --git a/source/common/listformatter.cpp b/source/common/listformatter.cpp |
index f4350d25cccc24493b9d68971df3799c51d48718..cfbd08bdce3cf31fea0b2376d6d5ce423fc602a3 100644 |
--- a/source/common/listformatter.cpp |
+++ b/source/common/listformatter.cpp |
@@ -1,7 +1,7 @@ |
/* |
******************************************************************************* |
* |
-* Copyright (C) 2013, International Business Machines |
+* Copyright (C) 2013-2014, International Business Machines |
* Corporation and others. All Rights Reserved. |
* |
******************************************************************************* |
@@ -15,6 +15,7 @@ |
*/ |
#include "unicode/listformatter.h" |
+#include "simplepatternformatter.h" |
#include "mutex.h" |
#include "hash.h" |
#include "cstring.h" |
@@ -25,10 +26,39 @@ |
U_NAMESPACE_BEGIN |
+struct ListFormatInternal : public UMemory { |
+ SimplePatternFormatter twoPattern; |
+ SimplePatternFormatter startPattern; |
+ SimplePatternFormatter middlePattern; |
+ SimplePatternFormatter endPattern; |
+ |
+ListFormatInternal( |
+ const UnicodeString& two, |
+ const UnicodeString& start, |
+ const UnicodeString& middle, |
+ const UnicodeString& end) : |
+ twoPattern(two), |
+ startPattern(start), |
+ middlePattern(middle), |
+ endPattern(end) {} |
+ |
+ListFormatInternal(const ListFormatData &data) : |
+ twoPattern(data.twoPattern), |
+ startPattern(data.startPattern), |
+ middlePattern(data.middlePattern), |
+ endPattern(data.endPattern) { } |
+ |
+ListFormatInternal(const ListFormatInternal &other) : |
+ twoPattern(other.twoPattern), |
+ startPattern(other.startPattern), |
+ middlePattern(other.middlePattern), |
+ endPattern(other.endPattern) { } |
+}; |
+ |
+ |
+ |
static Hashtable* listPatternHash = NULL; |
static UMutex listFormatterMutex = U_MUTEX_INITIALIZER; |
-static UChar FIRST_PARAMETER[] = { 0x7b, 0x30, 0x7d }; // "{0}" |
-static UChar SECOND_PARAMETER[] = { 0x7b, 0x31, 0x7d }; // "{0}" |
static const char *STANDARD_STYLE = "standard"; |
U_CDECL_BEGIN |
@@ -39,20 +69,43 @@ static UBool U_CALLCONV uprv_listformatter_cleanup() { |
} |
static void U_CALLCONV |
-uprv_deleteListFormatData(void *obj) { |
- delete static_cast<ListFormatData *>(obj); |
+uprv_deleteListFormatInternal(void *obj) { |
+ delete static_cast<ListFormatInternal *>(obj); |
} |
U_CDECL_END |
-static ListFormatData* loadListFormatData(const Locale& locale, const char* style, UErrorCode& errorCode); |
-static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode); |
- |
-ListFormatter::ListFormatter(const ListFormatter& other) : data(other.data) { |
+static ListFormatInternal* loadListFormatInternal( |
+ const Locale& locale, |
+ const char* style, |
+ UErrorCode& errorCode); |
+ |
+static void getStringByKey( |
+ const UResourceBundle* rb, |
+ const char* key, |
+ UnicodeString& result, |
+ UErrorCode& errorCode); |
+ |
+ListFormatter::ListFormatter(const ListFormatter& other) : |
+ owned(other.owned), data(other.data) { |
+ if (other.owned != NULL) { |
+ owned = new ListFormatInternal(*other.owned); |
+ data = owned; |
+ } |
} |
ListFormatter& ListFormatter::operator=(const ListFormatter& other) { |
- data = other.data; |
+ if (this == &other) { |
+ return *this; |
+ } |
+ delete owned; |
+ if (other.owned) { |
+ owned = new ListFormatInternal(*other.owned); |
+ data = owned; |
+ } else { |
+ owned = NULL; |
+ data = other.data; |
+ } |
return *this; |
} |
@@ -67,12 +120,12 @@ void ListFormatter::initializeHash(UErrorCode& errorCode) { |
return; |
} |
- listPatternHash->setValueDeleter(uprv_deleteListFormatData); |
+ listPatternHash->setValueDeleter(uprv_deleteListFormatInternal); |
ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup); |
} |
-const ListFormatData* ListFormatter::getListFormatData( |
+const ListFormatInternal* ListFormatter::getListFormatInternal( |
const Locale& locale, const char *style, UErrorCode& errorCode) { |
if (U_FAILURE(errorCode)) { |
return NULL; |
@@ -80,7 +133,7 @@ const ListFormatData* ListFormatter::getListFormatData( |
CharString keyBuffer(locale.getName(), errorCode); |
keyBuffer.append(':', errorCode).append(style, errorCode); |
UnicodeString key(keyBuffer.data(), -1, US_INV); |
- ListFormatData* result = NULL; |
+ ListFormatInternal* result = NULL; |
{ |
Mutex m(&listFormatterMutex); |
if (listPatternHash == NULL) { |
@@ -89,19 +142,19 @@ const ListFormatData* ListFormatter::getListFormatData( |
return NULL; |
} |
} |
- result = static_cast<ListFormatData*>(listPatternHash->get(key)); |
+ result = static_cast<ListFormatInternal*>(listPatternHash->get(key)); |
} |
if (result != NULL) { |
return result; |
} |
- result = loadListFormatData(locale, style, errorCode); |
+ result = loadListFormatInternal(locale, style, errorCode); |
if (U_FAILURE(errorCode)) { |
return NULL; |
} |
{ |
Mutex m(&listFormatterMutex); |
- ListFormatData* temp = static_cast<ListFormatData*>(listPatternHash->get(key)); |
+ ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key)); |
if (temp != NULL) { |
delete result; |
result = temp; |
@@ -115,7 +168,7 @@ const ListFormatData* ListFormatter::getListFormatData( |
return result; |
} |
-static ListFormatData* loadListFormatData( |
+static ListFormatInternal* loadListFormatInternal( |
const Locale& locale, const char * style, UErrorCode& errorCode) { |
UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode); |
if (U_FAILURE(errorCode)) { |
@@ -144,7 +197,7 @@ static ListFormatData* loadListFormatData( |
if (U_FAILURE(errorCode)) { |
return NULL; |
} |
- ListFormatData* result = new ListFormatData(two, start, middle, end); |
+ ListFormatInternal* result = new ListFormatInternal(two, start, middle, end); |
if (result == NULL) { |
errorCode = U_MEMORY_ALLOCATION_ERROR; |
return NULL; |
@@ -172,11 +225,11 @@ ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& e |
ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) { |
Locale tempLocale = locale; |
- const ListFormatData* listFormatData = getListFormatData(tempLocale, style, errorCode); |
+ const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode); |
if (U_FAILURE(errorCode)) { |
return NULL; |
} |
- ListFormatter* p = new ListFormatter(listFormatData); |
+ ListFormatter* p = new ListFormatter(listFormatInternal); |
if (p == NULL) { |
errorCode = U_MEMORY_ALLOCATION_ERROR; |
return NULL; |
@@ -184,14 +237,76 @@ ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *s |
return p; |
} |
+ListFormatter::ListFormatter(const ListFormatData& listFormatData) { |
+ owned = new ListFormatInternal(listFormatData); |
+ data = owned; |
+} |
-ListFormatter::ListFormatter(const ListFormatData* listFormatterData) : data(listFormatterData) { |
+ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) { |
} |
-ListFormatter::~ListFormatter() {} |
+ListFormatter::~ListFormatter() { |
+ delete owned; |
+} |
-UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems, |
- UnicodeString& appendTo, UErrorCode& errorCode) const { |
+/** |
+ * Joins first and second using the pattern pat. |
+ * On entry offset is an offset into first or -1 if offset unspecified. |
+ * On exit offset is offset of second in result if recordOffset was set |
+ * Otherwise if it was >=0 it is set to point into result where it used |
+ * to point into first. |
+ */ |
+static void joinStrings( |
+ const SimplePatternFormatter& pat, |
+ const UnicodeString& first, |
+ const UnicodeString& second, |
+ UnicodeString &result, |
+ UBool recordOffset, |
+ int32_t &offset, |
+ UErrorCode& errorCode) { |
+ if (U_FAILURE(errorCode)) { |
+ return; |
+ } |
+ const UnicodeString *params[2] = {&first, &second}; |
+ int32_t offsets[2]; |
+ pat.format( |
+ params, |
+ UPRV_LENGTHOF(params), |
+ result, |
+ offsets, |
+ UPRV_LENGTHOF(offsets), |
+ errorCode); |
+ if (U_FAILURE(errorCode)) { |
+ return; |
+ } |
+ if (offsets[0] == -1 || offsets[1] == -1) { |
+ errorCode = U_INVALID_FORMAT_ERROR; |
+ return; |
+ } |
+ if (recordOffset) { |
+ offset = offsets[1]; |
+ } else if (offset >= 0) { |
+ offset += offsets[0]; |
+ } |
+} |
+ |
+UnicodeString& ListFormatter::format( |
+ const UnicodeString items[], |
+ int32_t nItems, |
+ UnicodeString& appendTo, |
+ UErrorCode& errorCode) const { |
+ int32_t offset; |
+ return format(items, nItems, appendTo, -1, offset, errorCode); |
+} |
+ |
+UnicodeString& ListFormatter::format( |
+ const UnicodeString items[], |
+ int32_t nItems, |
+ UnicodeString& appendTo, |
+ int32_t index, |
+ int32_t &offset, |
+ UErrorCode& errorCode) const { |
+ offset = -1; |
if (U_FAILURE(errorCode)) { |
return appendTo; |
} |
@@ -200,67 +315,81 @@ UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems |
return appendTo; |
} |
- if (nItems > 0) { |
- UnicodeString newString = items[0]; |
- if (nItems == 2) { |
- addNewString(data->twoPattern, newString, items[1], errorCode); |
- } else if (nItems > 2) { |
- addNewString(data->startPattern, newString, items[1], errorCode); |
- int32_t i; |
- for (i = 2; i < nItems - 1; ++i) { |
- addNewString(data->middlePattern, newString, items[i], errorCode); |
- } |
- addNewString(data->endPattern, newString, items[nItems - 1], errorCode); |
+ if (nItems <= 0) { |
+ return appendTo; |
+ } |
+ if (nItems == 1) { |
+ if (index == 0) { |
+ offset = appendTo.length(); |
} |
- if (U_SUCCESS(errorCode)) { |
- appendTo += newString; |
+ appendTo.append(items[0]); |
+ return appendTo; |
+ } |
+ if (nItems == 2) { |
+ if (index == 0) { |
+ offset = 0; |
} |
+ joinStrings( |
+ data->twoPattern, |
+ items[0], |
+ items[1], |
+ appendTo, |
+ index == 1, |
+ offset, |
+ errorCode); |
+ return appendTo; |
} |
- return appendTo; |
-} |
- |
-/** |
- * Joins originalString and nextString using the pattern pat and puts the result in |
- * originalString. |
- */ |
-void ListFormatter::addNewString(const UnicodeString& pat, UnicodeString& originalString, |
- const UnicodeString& nextString, UErrorCode& errorCode) const { |
- if (U_FAILURE(errorCode)) { |
- return; |
+ UnicodeString temp[2]; |
+ if (index == 0) { |
+ offset = 0; |
} |
- |
- int32_t p0Offset = pat.indexOf(FIRST_PARAMETER, 3, 0); |
- if (p0Offset < 0) { |
- errorCode = U_ILLEGAL_ARGUMENT_ERROR; |
- return; |
+ joinStrings( |
+ data->startPattern, |
+ items[0], |
+ items[1], |
+ temp[0], |
+ index == 1, |
+ offset, |
+ errorCode); |
+ int32_t i; |
+ int32_t pos = 0; |
+ int32_t npos = 0; |
+ UBool startsWithZeroPlaceholder = |
+ data->middlePattern.startsWithPlaceholder(0); |
+ for (i = 2; i < nItems - 1; ++i) { |
+ if (!startsWithZeroPlaceholder) { |
+ npos = (pos + 1) & 1; |
+ temp[npos].remove(); |
+ } |
+ joinStrings( |
+ data->middlePattern, |
+ temp[pos], |
+ items[i], |
+ temp[npos], |
+ index == i, |
+ offset, |
+ errorCode); |
+ pos = npos; |
} |
- int32_t p1Offset = pat.indexOf(SECOND_PARAMETER, 3, 0); |
- if (p1Offset < 0) { |
- errorCode = U_ILLEGAL_ARGUMENT_ERROR; |
- return; |
+ if (!data->endPattern.startsWithPlaceholder(0)) { |
+ npos = (pos + 1) & 1; |
+ temp[npos].remove(); |
} |
- |
- int32_t i, j; |
- |
- const UnicodeString* firstString; |
- const UnicodeString* secondString; |
- if (p0Offset < p1Offset) { |
- i = p0Offset; |
- j = p1Offset; |
- firstString = &originalString; |
- secondString = &nextString; |
- } else { |
- i = p1Offset; |
- j = p0Offset; |
- firstString = &nextString; |
- secondString = &originalString; |
+ joinStrings( |
+ data->endPattern, |
+ temp[pos], |
+ items[nItems - 1], |
+ temp[npos], |
+ index == nItems - 1, |
+ offset, |
+ errorCode); |
+ if (U_SUCCESS(errorCode)) { |
+ if (offset >= 0) { |
+ offset += appendTo.length(); |
+ } |
+ appendTo += temp[npos]; |
} |
- |
- UnicodeString result = UnicodeString(pat, 0, i) + *firstString; |
- result += UnicodeString(pat, i+3, j-i-3); |
- result += *secondString; |
- result += UnicodeString(pat, j+3); |
- originalString = result; |
+ return appendTo; |
} |
U_NAMESPACE_END |