2012-08-27 20:14:25 +00:00
|
|
|
/*
|
|
|
|
*******************************************************************************
|
|
|
|
*
|
2013-07-25 16:36:25 +00:00
|
|
|
* Copyright (C) 2013, International Business Machines
|
2012-08-27 20:14:25 +00:00
|
|
|
* Corporation and others. All Rights Reserved.
|
|
|
|
*
|
|
|
|
*******************************************************************************
|
|
|
|
* file name: listformatter.cpp
|
|
|
|
* encoding: US-ASCII
|
|
|
|
* tab size: 8 (not used)
|
|
|
|
* indentation:4
|
|
|
|
*
|
|
|
|
* created on: 2012aug27
|
|
|
|
* created by: Umesh P. Nair
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "unicode/listformatter.h"
|
|
|
|
#include "mutex.h"
|
|
|
|
#include "hash.h"
|
|
|
|
#include "cstring.h"
|
|
|
|
#include "ulocimp.h"
|
|
|
|
#include "charstr.h"
|
|
|
|
#include "ucln_cmn.h"
|
2012-11-15 18:15:37 +00:00
|
|
|
#include "uresimp.h"
|
2012-08-27 20:14:25 +00:00
|
|
|
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
static Hashtable* listPatternHash = NULL;
|
2012-10-05 21:22:02 +00:00
|
|
|
static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
|
2012-08-27 20:14:25 +00:00
|
|
|
static UChar FIRST_PARAMETER[] = { 0x7b, 0x30, 0x7d }; // "{0}"
|
|
|
|
static UChar SECOND_PARAMETER[] = { 0x7b, 0x31, 0x7d }; // "{0}"
|
2013-07-25 16:36:25 +00:00
|
|
|
static const char *STANDARD_STYLE = "standard";
|
2012-08-27 20:14:25 +00:00
|
|
|
|
|
|
|
U_CDECL_BEGIN
|
2013-10-20 21:15:16 +00:00
|
|
|
static UBool U_CALLCONV uprv_listformatter_cleanup() {
|
2012-08-27 20:14:25 +00:00
|
|
|
delete listPatternHash;
|
|
|
|
listPatternHash = NULL;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void U_CALLCONV
|
|
|
|
uprv_deleteListFormatData(void *obj) {
|
|
|
|
delete static_cast<ListFormatData *>(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
U_CDECL_END
|
|
|
|
|
2013-07-25 16:36:25 +00:00
|
|
|
static ListFormatData* loadListFormatData(const Locale& locale, const char* style, UErrorCode& errorCode);
|
2012-11-15 18:15:37 +00:00
|
|
|
static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode);
|
|
|
|
|
2013-07-25 16:36:25 +00:00
|
|
|
ListFormatter::ListFormatter(const ListFormatter& other) : data(other.data) {
|
|
|
|
}
|
|
|
|
|
|
|
|
ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
|
|
|
|
data = other.data;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2012-08-27 20:14:25 +00:00
|
|
|
void ListFormatter::initializeHash(UErrorCode& errorCode) {
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
listPatternHash = new Hashtable();
|
|
|
|
if (listPatternHash == NULL) {
|
|
|
|
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
listPatternHash->setValueDeleter(uprv_deleteListFormatData);
|
|
|
|
ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const ListFormatData* ListFormatter::getListFormatData(
|
2013-07-25 16:36:25 +00:00
|
|
|
const Locale& locale, const char *style, UErrorCode& errorCode) {
|
2012-08-28 16:47:58 +00:00
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-07-25 16:36:25 +00:00
|
|
|
CharString keyBuffer(locale.getName(), errorCode);
|
|
|
|
keyBuffer.append(':', errorCode).append(style, errorCode);
|
|
|
|
UnicodeString key(keyBuffer.data(), -1, US_INV);
|
2012-11-15 18:15:37 +00:00
|
|
|
ListFormatData* result = NULL;
|
2012-08-27 20:14:25 +00:00
|
|
|
{
|
|
|
|
Mutex m(&listFormatterMutex);
|
|
|
|
if (listPatternHash == NULL) {
|
|
|
|
initializeHash(errorCode);
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2012-11-15 18:15:37 +00:00
|
|
|
result = static_cast<ListFormatData*>(listPatternHash->get(key));
|
|
|
|
}
|
|
|
|
if (result != NULL) {
|
|
|
|
return result;
|
|
|
|
}
|
2013-07-25 16:36:25 +00:00
|
|
|
result = loadListFormatData(locale, style, errorCode);
|
2012-11-15 18:15:37 +00:00
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return NULL;
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
|
|
|
|
2012-11-15 18:15:37 +00:00
|
|
|
{
|
|
|
|
Mutex m(&listFormatterMutex);
|
|
|
|
ListFormatData* temp = static_cast<ListFormatData*>(listPatternHash->get(key));
|
|
|
|
if (temp != NULL) {
|
|
|
|
delete result;
|
|
|
|
result = temp;
|
|
|
|
} else {
|
|
|
|
listPatternHash->put(key, result, errorCode);
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-07-25 16:36:25 +00:00
|
|
|
static ListFormatData* loadListFormatData(
|
|
|
|
const Locale& locale, const char * style, UErrorCode& errorCode) {
|
2012-11-15 18:15:37 +00:00
|
|
|
UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
ures_close(rb);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
|
2013-07-25 16:36:25 +00:00
|
|
|
rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode);
|
|
|
|
|
|
|
|
// TODO(Travis Keep): This is a hack until fallbacks can be added for
|
|
|
|
// listPattern/duration and listPattern/duration-narrow in CLDR.
|
|
|
|
if (errorCode == U_MISSING_RESOURCE_ERROR) {
|
|
|
|
errorCode = U_ZERO_ERROR;
|
|
|
|
rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode);
|
|
|
|
}
|
2012-11-15 18:15:37 +00:00
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
ures_close(rb);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
UnicodeString two, start, middle, end;
|
|
|
|
getStringByKey(rb, "2", two, errorCode);
|
|
|
|
getStringByKey(rb, "start", start, errorCode);
|
|
|
|
getStringByKey(rb, "middle", middle, errorCode);
|
|
|
|
getStringByKey(rb, "end", end, errorCode);
|
|
|
|
ures_close(rb);
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ListFormatData* result = new ListFormatData(two, start, middle, end);
|
|
|
|
if (result == NULL) {
|
|
|
|
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) {
|
|
|
|
int32_t len;
|
|
|
|
const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode);
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
result.setTo(ustr, len);
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
|
|
|
|
Locale locale; // The default locale.
|
|
|
|
return createInstance(locale, errorCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
|
2013-07-25 16:36:25 +00:00
|
|
|
return createInstance(locale, STANDARD_STYLE, errorCode);
|
|
|
|
}
|
|
|
|
|
|
|
|
ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
|
2012-08-27 20:14:25 +00:00
|
|
|
Locale tempLocale = locale;
|
2013-07-25 16:36:25 +00:00
|
|
|
const ListFormatData* listFormatData = getListFormatData(tempLocale, style, errorCode);
|
2012-11-15 18:15:37 +00:00
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-07-25 16:36:25 +00:00
|
|
|
ListFormatter* p = new ListFormatter(listFormatData);
|
2012-11-15 18:15:37 +00:00
|
|
|
if (p == NULL) {
|
|
|
|
errorCode = U_MEMORY_ALLOCATION_ERROR;
|
|
|
|
return NULL;
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
2012-11-15 18:15:37 +00:00
|
|
|
return p;
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
|
|
|
|
2013-07-25 16:36:25 +00:00
|
|
|
|
|
|
|
ListFormatter::ListFormatter(const ListFormatData* listFormatterData) : data(listFormatterData) {
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ListFormatter::~ListFormatter() {}
|
|
|
|
|
|
|
|
UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems,
|
|
|
|
UnicodeString& appendTo, UErrorCode& errorCode) const {
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return appendTo;
|
|
|
|
}
|
2013-07-25 16:36:25 +00:00
|
|
|
if (data == NULL) {
|
|
|
|
errorCode = U_INVALID_STATE_ERROR;
|
|
|
|
return appendTo;
|
|
|
|
}
|
2012-08-27 20:14:25 +00:00
|
|
|
|
|
|
|
if (nItems > 0) {
|
|
|
|
UnicodeString newString = items[0];
|
|
|
|
if (nItems == 2) {
|
2013-07-25 16:36:25 +00:00
|
|
|
addNewString(data->twoPattern, newString, items[1], errorCode);
|
2012-08-27 20:14:25 +00:00
|
|
|
} else if (nItems > 2) {
|
2013-07-25 16:36:25 +00:00
|
|
|
addNewString(data->startPattern, newString, items[1], errorCode);
|
2012-11-15 18:15:37 +00:00
|
|
|
int32_t i;
|
2012-08-27 20:14:25 +00:00
|
|
|
for (i = 2; i < nItems - 1; ++i) {
|
2013-07-25 16:36:25 +00:00
|
|
|
addNewString(data->middlePattern, newString, items[i], errorCode);
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
2013-07-25 16:36:25 +00:00
|
|
|
addNewString(data->endPattern, newString, items[nItems - 1], errorCode);
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
|
|
|
if (U_SUCCESS(errorCode)) {
|
|
|
|
appendTo += newString;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Joins originalString and nextString using the pattern pat and puts the result in
|
|
|
|
* originalString.
|
|
|
|
*/
|
|
|
|
void ListFormatter::addNewString(const UnicodeString& pat, UnicodeString& originalString,
|
2012-08-28 16:47:58 +00:00
|
|
|
const UnicodeString& nextString, UErrorCode& errorCode) const {
|
2012-08-27 20:14:25 +00:00
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t p0Offset = pat.indexOf(FIRST_PARAMETER, 3, 0);
|
|
|
|
if (p0Offset < 0) {
|
|
|
|
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int32_t p1Offset = pat.indexOf(SECOND_PARAMETER, 3, 0);
|
|
|
|
if (p1Offset < 0) {
|
|
|
|
errorCode = U_ILLEGAL_ARGUMENT_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
UnicodeString result = UnicodeString(pat, 0, i) + *firstString;
|
|
|
|
result += UnicodeString(pat, i+3, j-i-3);
|
|
|
|
result += *secondString;
|
|
|
|
result += UnicodeString(pat, j+3);
|
|
|
|
originalString = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
U_NAMESPACE_END
|