2012-08-27 20:14:25 +00:00
|
|
|
/*
|
|
|
|
*******************************************************************************
|
|
|
|
*
|
2014-01-29 05:08:55 +00:00
|
|
|
* Copyright (C) 2013-2014, 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"
|
2014-01-29 22:57:06 +00:00
|
|
|
#include "simplepatternformatter.h"
|
2012-08-27 20:14:25 +00:00
|
|
|
#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
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
|
|
|
|
|
2012-08-27 20:14:25 +00:00
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
struct ListFormatInternal : public UMemory {
|
2014-01-29 22:57:06 +00:00
|
|
|
SimplePatternFormatter twoPattern;
|
|
|
|
SimplePatternFormatter startPattern;
|
|
|
|
SimplePatternFormatter middlePattern;
|
|
|
|
SimplePatternFormatter endPattern;
|
2014-01-29 05:08:55 +00:00
|
|
|
|
|
|
|
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) { }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-27 20:14:25 +00:00
|
|
|
static Hashtable* listPatternHash = NULL;
|
2012-10-05 21:22:02 +00:00
|
|
|
static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
|
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
|
2014-01-29 05:08:55 +00:00
|
|
|
uprv_deleteListFormatInternal(void *obj) {
|
|
|
|
delete static_cast<ListFormatInternal *>(obj);
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
U_CDECL_END
|
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
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;
|
|
|
|
}
|
2013-07-25 16:36:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
|
2014-01-29 05:08:55 +00:00
|
|
|
if (this == &other) {
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
delete owned;
|
|
|
|
if (other.owned) {
|
|
|
|
owned = new ListFormatInternal(*other.owned);
|
|
|
|
data = owned;
|
|
|
|
} else {
|
|
|
|
owned = NULL;
|
|
|
|
data = other.data;
|
|
|
|
}
|
2013-07-25 16:36:25 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
listPatternHash->setValueDeleter(uprv_deleteListFormatInternal);
|
2012-08-27 20:14:25 +00:00
|
|
|
ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
const ListFormatInternal* ListFormatter::getListFormatInternal(
|
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);
|
2014-01-29 05:08:55 +00:00
|
|
|
ListFormatInternal* result = NULL;
|
2012-08-27 20:14:25 +00:00
|
|
|
{
|
|
|
|
Mutex m(&listFormatterMutex);
|
|
|
|
if (listPatternHash == NULL) {
|
|
|
|
initializeHash(errorCode);
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2014-01-29 05:08:55 +00:00
|
|
|
result = static_cast<ListFormatInternal*>(listPatternHash->get(key));
|
2012-11-15 18:15:37 +00:00
|
|
|
}
|
|
|
|
if (result != NULL) {
|
|
|
|
return result;
|
|
|
|
}
|
2014-01-29 05:08:55 +00:00
|
|
|
result = loadListFormatInternal(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);
|
2014-01-29 05:08:55 +00:00
|
|
|
ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key));
|
2012-11-15 18:15:37 +00:00
|
|
|
if (temp != NULL) {
|
|
|
|
delete result;
|
|
|
|
result = temp;
|
|
|
|
} else {
|
|
|
|
listPatternHash->put(key, result, errorCode);
|
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
static ListFormatInternal* loadListFormatInternal(
|
2013-07-25 16:36:25 +00:00
|
|
|
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;
|
|
|
|
}
|
2014-01-29 05:08:55 +00:00
|
|
|
ListFormatInternal* result = new ListFormatInternal(two, start, middle, end);
|
2012-11-15 18:15:37 +00:00
|
|
|
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;
|
2014-01-29 05:08:55 +00:00
|
|
|
const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode);
|
2012-11-15 18:15:37 +00:00
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-01-29 05:08:55 +00:00
|
|
|
ListFormatter* p = new ListFormatter(listFormatInternal);
|
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
|
|
|
}
|
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
ListFormatter::ListFormatter(const ListFormatData& listFormatData) {
|
|
|
|
owned = new ListFormatInternal(listFormatData);
|
|
|
|
data = owned;
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) {
|
|
|
|
}
|
2012-08-27 20:14:25 +00:00
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
ListFormatter::~ListFormatter() {
|
|
|
|
delete owned;
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-01-29 05:08:55 +00:00
|
|
|
* Joins first and second using the pattern pat.
|
2014-02-04 00:29:17 +00:00
|
|
|
* 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.
|
2012-08-27 20:14:25 +00:00
|
|
|
*/
|
2014-01-29 05:08:55 +00:00
|
|
|
static void joinStrings(
|
2014-01-29 22:57:06 +00:00
|
|
|
const SimplePatternFormatter& pat,
|
2014-01-29 05:08:55 +00:00
|
|
|
const UnicodeString& first,
|
|
|
|
const UnicodeString& second,
|
|
|
|
UnicodeString &result,
|
|
|
|
UBool recordOffset,
|
2014-02-04 00:29:17 +00:00
|
|
|
int32_t &offset,
|
2014-01-29 05:08:55 +00:00
|
|
|
UErrorCode& errorCode) {
|
2012-08-27 20:14:25 +00:00
|
|
|
if (U_FAILURE(errorCode)) {
|
|
|
|
return;
|
|
|
|
}
|
2014-01-29 05:08:55 +00:00
|
|
|
const UnicodeString *params[2] = {&first, &second};
|
|
|
|
int32_t offsets[2];
|
2014-01-29 22:57:06 +00:00
|
|
|
pat.format(
|
2014-01-29 05:08:55 +00:00
|
|
|
params,
|
|
|
|
LENGTHOF(params),
|
|
|
|
result,
|
|
|
|
offsets,
|
|
|
|
LENGTHOF(offsets),
|
|
|
|
errorCode);
|
|
|
|
if (U_FAILURE(errorCode)) {
|
2012-08-27 20:14:25 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-01-29 05:08:55 +00:00
|
|
|
if (offsets[0] == -1 || offsets[1] == -1) {
|
|
|
|
errorCode = U_INVALID_FORMAT_ERROR;
|
2012-08-27 20:14:25 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-01-29 05:08:55 +00:00
|
|
|
if (recordOffset) {
|
|
|
|
offset = offsets[1];
|
|
|
|
} else if (offset >= 0) {
|
|
|
|
offset += offsets[0];
|
|
|
|
}
|
|
|
|
}
|
2012-08-27 20:14:25 +00:00
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
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);
|
|
|
|
}
|
2012-08-27 20:14:25 +00:00
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
if (data == NULL) {
|
|
|
|
errorCode = U_INVALID_STATE_ERROR;
|
|
|
|
return appendTo;
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
|
|
|
|
2014-01-29 05:08:55 +00:00
|
|
|
if (nItems <= 0) {
|
|
|
|
return appendTo;
|
|
|
|
}
|
|
|
|
if (nItems == 1) {
|
|
|
|
if (index == 0) {
|
|
|
|
offset = appendTo.length();
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
UnicodeString temp[2];
|
|
|
|
if (index == 0) {
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
joinStrings(
|
|
|
|
data->startPattern,
|
|
|
|
items[0],
|
|
|
|
items[1],
|
|
|
|
temp[0],
|
|
|
|
index == 1,
|
|
|
|
offset,
|
|
|
|
errorCode);
|
|
|
|
int32_t i;
|
|
|
|
int32_t pos = 0;
|
|
|
|
int32_t npos = 1;
|
|
|
|
for (i = 2; i < nItems - 1; ++i) {
|
|
|
|
temp[npos].remove();
|
|
|
|
joinStrings(
|
|
|
|
data->middlePattern,
|
|
|
|
temp[pos],
|
|
|
|
items[i],
|
|
|
|
temp[npos],
|
|
|
|
index == i,
|
|
|
|
offset,
|
|
|
|
errorCode);
|
|
|
|
pos = npos;
|
|
|
|
npos = (pos + 1) & 1;
|
|
|
|
}
|
|
|
|
temp[npos].remove();
|
|
|
|
joinStrings(
|
|
|
|
data->endPattern,
|
|
|
|
temp[pos],
|
|
|
|
items[nItems - 1],
|
|
|
|
temp[npos],
|
|
|
|
index == nItems - 1,
|
|
|
|
offset,
|
|
|
|
errorCode);
|
|
|
|
if (U_SUCCESS(errorCode)) {
|
2014-02-04 00:29:17 +00:00
|
|
|
if (offset >= 0) {
|
|
|
|
offset += appendTo.length();
|
|
|
|
}
|
2014-01-29 05:08:55 +00:00
|
|
|
appendTo += temp[npos];
|
|
|
|
}
|
|
|
|
return appendTo;
|
2012-08-27 20:14:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
U_NAMESPACE_END
|