/*
*******************************************************************************
*
* Copyright (C) 2012, International Business Machines
* 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"
#include "uresimp.h"
U_NAMESPACE_BEGIN
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}"
U_CDECL_BEGIN
static UBool U_CALLCONV uprv_listformatter_cleanup() {
delete listPatternHash;
listPatternHash = NULL;
return TRUE;
}
static void U_CALLCONV
uprv_deleteListFormatData(void *obj) {
delete static_cast<ListFormatData *>(obj);
}
U_CDECL_END
static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode);
static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode);
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(
const Locale& locale, UErrorCode& errorCode) {
if (U_FAILURE(errorCode)) {
return NULL;
}
UnicodeString key(locale.getName(), -1, US_INV);
ListFormatData* result = NULL;
{
Mutex m(&listFormatterMutex);
if (listPatternHash == NULL) {
initializeHash(errorCode);
if (U_FAILURE(errorCode)) {
return NULL;
}
}
result = static_cast<ListFormatData*>(listPatternHash->get(key));
}
if (result != NULL) {
return result;
}
result = loadListFormatData(locale, errorCode);
if (U_FAILURE(errorCode)) {
return NULL;
}
{
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;
}
static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode) {
UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
if (U_FAILURE(errorCode)) {
ures_close(rb);
return NULL;
}
rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode);
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);
}
ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
Locale locale; // The default locale.
return createInstance(locale, errorCode);
}
ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
Locale tempLocale = locale;
const ListFormatData* listFormatData = getListFormatData(tempLocale, errorCode);
if (U_FAILURE(errorCode)) {
return NULL;
}
ListFormatter* p = new ListFormatter(*listFormatData);
if (p == NULL) {
errorCode = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
return p;
}
ListFormatter::ListFormatter(const ListFormatData& listFormatterData) : data(listFormatterData) {
}
ListFormatter::~ListFormatter() {}
UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems,
UnicodeString& appendTo, UErrorCode& errorCode) const {
if (U_FAILURE(errorCode)) {
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 (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,
const UnicodeString& nextString, UErrorCode& errorCode) const {
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