mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-13 17:01:16 +00:00
ICU-10646 Introduce Template class and change ListFormatter to use it. Introduce QuantityFormatter class and change RelativeDateTimeFormatter to use it.
X-SVN-Rev: 35018
This commit is contained in:
parent
1cf0f06b01
commit
d4ec281c99
14 changed files with 1176 additions and 190 deletions
5
.gitattributes
vendored
5
.gitattributes
vendored
|
@ -53,6 +53,8 @@ icu4c/source/aclocal.m4 -text
|
|||
icu4c/source/allinone/icucheck.bat -text
|
||||
icu4c/source/common/common.vcxproj -text
|
||||
icu4c/source/common/common.vcxproj.filters -text
|
||||
icu4c/source/common/template.cpp -text
|
||||
icu4c/source/common/template.h -text
|
||||
icu4c/source/data/curr/pool.res -text
|
||||
icu4c/source/data/in/coll/invuca.icu -text
|
||||
icu4c/source/data/in/coll/ucadata.icu -text
|
||||
|
@ -75,6 +77,8 @@ icu4c/source/extra/uconv/uconv.vcxproj -text
|
|||
icu4c/source/extra/uconv/uconv.vcxproj.filters -text
|
||||
icu4c/source/i18n/i18n.vcxproj -text
|
||||
icu4c/source/i18n/i18n.vcxproj.filters -text
|
||||
icu4c/source/i18n/quantityformatter.cpp -text
|
||||
icu4c/source/i18n/quantityformatter.h -text
|
||||
icu4c/source/io/io.vcxproj -text
|
||||
icu4c/source/io/io.vcxproj.filters -text
|
||||
icu4c/source/layout/layout.vcxproj -text
|
||||
|
@ -142,6 +146,7 @@ icu4c/source/test/cintltst/cintltst.vcxproj -text
|
|||
icu4c/source/test/cintltst/cintltst.vcxproj.filters -text
|
||||
icu4c/source/test/intltest/intltest.vcxproj -text
|
||||
icu4c/source/test/intltest/intltest.vcxproj.filters -text
|
||||
icu4c/source/test/intltest/templatetest.cpp -text
|
||||
icu4c/source/test/iotest/iotest.vcxproj -text
|
||||
icu4c/source/test/iotest/iotest.vcxproj.filters -text
|
||||
icu4c/source/test/letest/cletest.vcxproj -text
|
||||
|
|
|
@ -105,7 +105,7 @@ serv.o servnotf.o servls.o servlk.o servlkf.o servrbf.o servslkf.o \
|
|||
uidna.o usprep.o uts46.o punycode.o \
|
||||
util.o util_props.o parsepos.o locbased.o cwchar.o wintz.o dtintrv.o ucnvsel.o propsvec.o \
|
||||
ulist.o uloc_tag.o icudataver.o icuplug.o listformatter.o lrucache.o \
|
||||
sharedobject.o
|
||||
sharedobject.o template.o
|
||||
|
||||
## Header files to install
|
||||
HEADERS = $(srcdir)/unicode/*.h
|
||||
|
|
|
@ -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 "template.h"
|
||||
#include "mutex.h"
|
||||
#include "hash.h"
|
||||
#include "cstring.h"
|
||||
|
@ -23,12 +24,43 @@
|
|||
#include "ucln_cmn.h"
|
||||
#include "uresimp.h"
|
||||
|
||||
#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
struct ListFormatInternal : public UMemory {
|
||||
Template twoPattern;
|
||||
Template startPattern;
|
||||
Template middlePattern;
|
||||
Template 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 +71,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);
|
||||
static ListFormatInternal* loadListFormatInternal(
|
||||
const Locale& locale,
|
||||
const char* style,
|
||||
UErrorCode& errorCode);
|
||||
|
||||
ListFormatter::ListFormatter(const ListFormatter& other) : data(other.data) {
|
||||
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 +122,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 +135,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 +144,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 +170,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 +199,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 +227,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 +239,72 @@ ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *s
|
|||
return p;
|
||||
}
|
||||
|
||||
|
||||
ListFormatter::ListFormatter(const ListFormatData* listFormatterData) : data(listFormatterData) {
|
||||
ListFormatter::ListFormatter(const ListFormatData& listFormatData) {
|
||||
owned = new ListFormatInternal(listFormatData);
|
||||
data = owned;
|
||||
}
|
||||
|
||||
ListFormatter::~ListFormatter() {}
|
||||
ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) {
|
||||
}
|
||||
|
||||
UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems,
|
||||
UnicodeString& appendTo, UErrorCode& errorCode) const {
|
||||
ListFormatter::~ListFormatter() {
|
||||
delete owned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins first and second using the pattern pat.
|
||||
*/
|
||||
static void joinStrings(
|
||||
const Template& 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.evaluate(
|
||||
params,
|
||||
LENGTHOF(params),
|
||||
result,
|
||||
offsets,
|
||||
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 +313,72 @@ 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;
|
||||
}
|
||||
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)) {
|
||||
offset += appendTo.length();
|
||||
appendTo += temp[npos];
|
||||
}
|
||||
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
|
||||
|
|
303
icu4c/source/common/template.cpp
Normal file
303
icu4c/source/common/template.cpp
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
******************************************************************************
|
||||
* Copyright (C) 2014, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
******************************************************************************
|
||||
* template.cpp
|
||||
*/
|
||||
#include "template.h"
|
||||
#include "cstring.h"
|
||||
#include "uassert.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
typedef enum TemplateCompileState {
|
||||
INIT,
|
||||
APOSTROPHE,
|
||||
PLACEHOLDER
|
||||
} TemplateCompileState;
|
||||
|
||||
class TemplateIdBuilder {
|
||||
public:
|
||||
TemplateIdBuilder() : id(0), idLen(0) { }
|
||||
~TemplateIdBuilder() { }
|
||||
void reset() { id = 0; idLen = 0; }
|
||||
int32_t getId() const { return id; }
|
||||
void appendTo(UChar *buffer, int32_t *len) const;
|
||||
UBool isValid() const { return (idLen > 0); }
|
||||
void add(UChar ch);
|
||||
private:
|
||||
int32_t id;
|
||||
int32_t idLen;
|
||||
TemplateIdBuilder(const TemplateIdBuilder &other);
|
||||
TemplateIdBuilder &operator=(const TemplateIdBuilder &other);
|
||||
};
|
||||
|
||||
void TemplateIdBuilder::appendTo(UChar *buffer, int32_t *len) const {
|
||||
int32_t origLen = *len;
|
||||
int32_t kId = id;
|
||||
for (int32_t i = origLen + idLen - 1; i >= origLen; i--) {
|
||||
int32_t digit = kId % 10;
|
||||
buffer[i] = digit + 0x30;
|
||||
kId /= 10;
|
||||
}
|
||||
*len = origLen + idLen;
|
||||
}
|
||||
|
||||
void TemplateIdBuilder::add(UChar ch) {
|
||||
id = id * 10 + (ch - 0x30);
|
||||
idLen++;
|
||||
}
|
||||
|
||||
Template::Template() :
|
||||
noPlaceholders(),
|
||||
placeholdersByOffset(placeholderBuffer),
|
||||
placeholderSize(0),
|
||||
placeholderCapacity(EXPECTED_PLACEHOLDER_COUNT),
|
||||
placeholderCount(0) {
|
||||
}
|
||||
|
||||
Template::Template(const UnicodeString &pattern) :
|
||||
noPlaceholders(),
|
||||
placeholdersByOffset(placeholderBuffer),
|
||||
placeholderSize(0),
|
||||
placeholderCapacity(EXPECTED_PLACEHOLDER_COUNT),
|
||||
placeholderCount(0) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
compile(pattern, status);
|
||||
}
|
||||
|
||||
Template::Template(const Template &other) :
|
||||
noPlaceholders(other.noPlaceholders),
|
||||
placeholdersByOffset(placeholderBuffer),
|
||||
placeholderSize(0),
|
||||
placeholderCapacity(EXPECTED_PLACEHOLDER_COUNT),
|
||||
placeholderCount(other.placeholderCount) {
|
||||
placeholderSize = ensureCapacity(other.placeholderSize);
|
||||
uprv_memcpy(
|
||||
placeholdersByOffset,
|
||||
other.placeholdersByOffset,
|
||||
placeholderSize * 2 * sizeof(int32_t));
|
||||
}
|
||||
|
||||
Template &Template::operator=(const Template& other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
noPlaceholders = other.noPlaceholders;
|
||||
placeholderCount = other.placeholderCount;
|
||||
placeholderSize = ensureCapacity(other.placeholderSize);
|
||||
uprv_memcpy(
|
||||
placeholdersByOffset,
|
||||
other.placeholdersByOffset,
|
||||
placeholderSize * 2 * sizeof(int32_t));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Template::~Template() {
|
||||
if (placeholdersByOffset != placeholderBuffer) {
|
||||
uprv_free(placeholdersByOffset);
|
||||
}
|
||||
}
|
||||
|
||||
UBool Template::compile(const UnicodeString &pattern, UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
const UChar *patternBuffer = pattern.getBuffer();
|
||||
int32_t patternLength = pattern.length();
|
||||
UChar *buffer = noPlaceholders.getBuffer(patternLength);
|
||||
int32_t len = 0;
|
||||
placeholderSize = 0;
|
||||
placeholderCount = 0;
|
||||
TemplateCompileState state = INIT;
|
||||
TemplateIdBuilder idBuilder;
|
||||
for (int32_t i = 0; i < patternLength; ++i) {
|
||||
UChar ch = patternBuffer[i];
|
||||
switch (state) {
|
||||
case INIT:
|
||||
if (ch == 0x27) {
|
||||
state = APOSTROPHE;
|
||||
} else if (ch == 0x7B) {
|
||||
state = PLACEHOLDER;
|
||||
idBuilder.reset();
|
||||
} else {
|
||||
buffer[len++] = ch;
|
||||
}
|
||||
break;
|
||||
case APOSTROPHE:
|
||||
if (ch == 0x27) {
|
||||
buffer[len++] = 0x27;
|
||||
} else if (ch == 0x7B) {
|
||||
buffer[len++] = 0x7B;
|
||||
} else {
|
||||
buffer[len++] = 0x27;
|
||||
buffer[len++] = ch;
|
||||
}
|
||||
state = INIT;
|
||||
break;
|
||||
case PLACEHOLDER:
|
||||
if (ch >= 0x30 && ch <= 0x39) {
|
||||
idBuilder.add(ch);
|
||||
} else if (ch == 0x7D && idBuilder.isValid()) {
|
||||
if (!addPlaceholder(idBuilder.getId(), len)) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return FALSE;
|
||||
}
|
||||
state = INIT;
|
||||
} else {
|
||||
buffer[len++] = 0x7B;
|
||||
idBuilder.appendTo(buffer, &len);
|
||||
buffer[len++] = ch;
|
||||
state = INIT;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (state) {
|
||||
case INIT:
|
||||
break;
|
||||
case APOSTROPHE:
|
||||
buffer[len++] = 0x27;
|
||||
break;
|
||||
case PLACEHOLDER:
|
||||
buffer[len++] = 0X7B;
|
||||
idBuilder.appendTo(buffer, &len);
|
||||
break;
|
||||
default:
|
||||
U_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
noPlaceholders.releaseBuffer(len);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UnicodeString& Template::evaluate(
|
||||
const UnicodeString * const *placeholderValues,
|
||||
int32_t placeholderValueCount,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) const {
|
||||
return evaluate(
|
||||
placeholderValues,
|
||||
placeholderValueCount,
|
||||
appendTo,
|
||||
NULL,
|
||||
0,
|
||||
status);
|
||||
}
|
||||
|
||||
static void updatePlaceholderOffset(
|
||||
int32_t placeholderId,
|
||||
int32_t placeholderOffset,
|
||||
int32_t *offsetArray,
|
||||
int32_t offsetArrayLength) {
|
||||
if (placeholderId < offsetArrayLength) {
|
||||
offsetArray[placeholderId] = placeholderOffset;
|
||||
}
|
||||
}
|
||||
|
||||
static void appendRange(
|
||||
const UnicodeString &src,
|
||||
int32_t start,
|
||||
int32_t end,
|
||||
UnicodeString &dest) {
|
||||
dest.append(src, start, end - start);
|
||||
}
|
||||
|
||||
UnicodeString& Template::evaluate(
|
||||
const UnicodeString * const *placeholderValues,
|
||||
int32_t placeholderValueCount,
|
||||
UnicodeString &appendTo,
|
||||
int32_t *offsetArray,
|
||||
int32_t offsetArrayLength,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
if (placeholderValueCount < placeholderCount) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
for (int32_t i = 0; i < offsetArrayLength; ++i) {
|
||||
offsetArray[i] = -1;
|
||||
}
|
||||
if (placeholderSize == 0) {
|
||||
appendTo.append(noPlaceholders);
|
||||
return appendTo;
|
||||
}
|
||||
appendRange(
|
||||
noPlaceholders,
|
||||
0,
|
||||
placeholdersByOffset[0],
|
||||
appendTo);
|
||||
updatePlaceholderOffset(
|
||||
placeholdersByOffset[1],
|
||||
appendTo.length(),
|
||||
offsetArray,
|
||||
offsetArrayLength);
|
||||
appendTo.append(*placeholderValues[placeholdersByOffset[1]]);
|
||||
for (int32_t i = 1; i < placeholderSize; ++i) {
|
||||
appendRange(
|
||||
noPlaceholders,
|
||||
placeholdersByOffset[2 * i - 2],
|
||||
placeholdersByOffset[2 * i],
|
||||
appendTo);
|
||||
updatePlaceholderOffset(
|
||||
placeholdersByOffset[2 * i + 1],
|
||||
appendTo.length(),
|
||||
offsetArray,
|
||||
offsetArrayLength);
|
||||
appendTo.append(*placeholderValues[placeholdersByOffset[2 * i + 1]]);
|
||||
}
|
||||
appendRange(
|
||||
noPlaceholders,
|
||||
placeholdersByOffset[2 * placeholderSize - 2],
|
||||
noPlaceholders.length(),
|
||||
appendTo);
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
int32_t Template::ensureCapacity(int32_t atLeast) {
|
||||
if (atLeast <= placeholderCapacity) {
|
||||
return atLeast;
|
||||
}
|
||||
// aim to double capacity each time
|
||||
int32_t newCapacity = 2*atLeast - 2;
|
||||
|
||||
// allocate new buffer
|
||||
int32_t *newBuffer = (int32_t *) uprv_malloc(2 * newCapacity * sizeof(int32_t));
|
||||
if (newBuffer == NULL) {
|
||||
return placeholderCapacity;
|
||||
}
|
||||
|
||||
// Copy contents of old buffer to new buffer
|
||||
uprv_memcpy(newBuffer, placeholdersByOffset, 2 * placeholderSize * sizeof(int32_t));
|
||||
|
||||
// free old buffer
|
||||
if (placeholdersByOffset != placeholderBuffer) {
|
||||
uprv_free(placeholdersByOffset);
|
||||
}
|
||||
|
||||
// Use new buffer
|
||||
placeholdersByOffset = newBuffer;
|
||||
placeholderCapacity = newCapacity;
|
||||
return atLeast;
|
||||
}
|
||||
|
||||
UBool Template::addPlaceholder(int32_t id, int32_t offset) {
|
||||
if (ensureCapacity(placeholderSize + 1) < placeholderSize + 1) {
|
||||
return FALSE;
|
||||
}
|
||||
++placeholderSize;
|
||||
placeholdersByOffset[2 * placeholderSize - 2] = offset;
|
||||
placeholdersByOffset[2 * placeholderSize - 1] = id;
|
||||
if (id >= placeholderCount) {
|
||||
placeholderCount = id + 1;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
150
icu4c/source/common/template.h
Normal file
150
icu4c/source/common/template.h
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
******************************************************************************
|
||||
* Copyright (C) 2014, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
******************************************************************************
|
||||
* template.h
|
||||
*/
|
||||
|
||||
#ifndef __TEMPLATE_H__
|
||||
#define __TEMPLATE_H__
|
||||
|
||||
#define EXPECTED_PLACEHOLDER_COUNT 3
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
#include "unicode/unistr.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* Compiled version of a template such as "{1} was born in {0}".
|
||||
* <p>
|
||||
* Using Template objects is both faster and safer than adhoc replacement.
|
||||
* They are faster because they are precompiled; they are safer because they
|
||||
* account for curly braces escaped by apostrophe (').
|
||||
*
|
||||
* Placeholders are of the form \{[0-9]+\}. If a curly brace is preceded
|
||||
* by a single quote, it becomes a curly brace instead of the start of a
|
||||
* placeholder. Two single quotes resolve to one single quote.
|
||||
* <p>
|
||||
* Concurrent calls only to const methods on a Template object are safe,
|
||||
* but concurrent const and non-const method calls on a Template object
|
||||
* are not safe and require synchronization.
|
||||
* <p>
|
||||
* Example:
|
||||
* <pre>
|
||||
* Template template("{1} '{born} in {0}");
|
||||
* UnicodeString england("england");
|
||||
* UnicodeString paul("paul");
|
||||
* UnicodeString *params[] = {&england, &paul};
|
||||
* UnicodeString result;
|
||||
* UErrorCode status = U_ZERO_ERROR;
|
||||
* // Evaluates to: "paul {born} in england"
|
||||
* template.evaluate(params, 2, result, status);
|
||||
* </pre>
|
||||
*/
|
||||
class U_COMMON_API Template : public UMemory {
|
||||
public:
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
Template();
|
||||
|
||||
/**
|
||||
* Construct from a pattern. Will never fail if pattern has three or
|
||||
* fewer placeholders in it.
|
||||
*/
|
||||
explicit Template(const UnicodeString& pattern);
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
Template(const Template& other);
|
||||
|
||||
/**
|
||||
* Assignment operator
|
||||
*/
|
||||
Template &operator=(const Template& other);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~Template();
|
||||
|
||||
/**
|
||||
* Compiles pattern and makes this object represent pattern.
|
||||
*
|
||||
* Returns TRUE on success; FALSE on failure. Will not fail if
|
||||
* there are three or fewer placeholders in pattern. May fail with
|
||||
* U_MEMORY_ALLOCATION_ERROR if there are more than three placeholders.
|
||||
*/
|
||||
UBool compile(const UnicodeString &pattern, UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Returns (maxPlaceholderId + 1). For example
|
||||
* <code>Template("{0} {2}").getPlaceholderCount() evaluates to 3.
|
||||
* Callers use this function to find out how many values are needed
|
||||
* to evaluate this template.
|
||||
*/
|
||||
int32_t getPlaceholderCount() const {
|
||||
return placeholderCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates this template according to the given placeholder values.
|
||||
*
|
||||
* The caller retains ownership of all pointers.
|
||||
* @param placeholderValues 1st one corresponds to {0}; 2nd to {1};
|
||||
* 3rd to {2} etc.
|
||||
* @param placeholderValueCount the number of placeholder values
|
||||
* must be at least large enough to provide values for all placeholders
|
||||
* in this object. Otherwise status set to U_ILLEGAL_ARGUMENT_ERROR.
|
||||
* @param appendTo resulting string appended here.
|
||||
* @param status any error stored here.
|
||||
*/
|
||||
UnicodeString &evaluate(
|
||||
const UnicodeString * const *placeholderValues,
|
||||
int32_t placeholderValueCount,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) const;
|
||||
|
||||
/**
|
||||
* Evaluates this template according to the given placeholder values.
|
||||
*
|
||||
* The caller retains ownership of all pointers.
|
||||
* @param placeholderValues 1st one corresponds to {0}; 2nd to {1};
|
||||
* 3rd to {2} etc.
|
||||
* @param placeholderValueCount the number of placeholder values
|
||||
* must be at least large enough to provide values for all placeholders
|
||||
* in this object. Otherwise status set to U_ILLEGAL_ARGUMENT_ERROR.
|
||||
* @param appendTo resulting string appended here.
|
||||
* @param offsetArray The offset of each placeholder value in appendTo
|
||||
* stored here. The first value gets the offset of the value for {0};
|
||||
* the 2nd for {1}; the 3rd for {2} etc. -1 means that the corresponding
|
||||
* placeholder does not exist in this object. If caller is not
|
||||
* interested in offsets, it may pass NULL and 0 for the length.
|
||||
* @param offsetArrayLength the size of offsetArray may be less than
|
||||
* placeholderValueCount.
|
||||
* @param status any error stored here.
|
||||
*/
|
||||
UnicodeString &evaluate(
|
||||
const UnicodeString * const *placeholderValues,
|
||||
int32_t placeholderValueCount,
|
||||
UnicodeString &appendTo,
|
||||
int32_t *offsetArray,
|
||||
int32_t offsetArrayLength,
|
||||
UErrorCode &status) const;
|
||||
private:
|
||||
UnicodeString noPlaceholders;
|
||||
int32_t placeholderBuffer[EXPECTED_PLACEHOLDER_COUNT * 2];
|
||||
int32_t *placeholdersByOffset;
|
||||
int32_t placeholderSize;
|
||||
int32_t placeholderCapacity;
|
||||
int32_t placeholderCount;
|
||||
int32_t ensureCapacity(int32_t size);
|
||||
UBool addPlaceholder(int32_t id, int32_t offset);
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
|
@ -29,6 +29,9 @@ U_NAMESPACE_BEGIN
|
|||
/** @internal */
|
||||
class Hashtable;
|
||||
|
||||
/** @internal */
|
||||
struct ListFormatInternal;
|
||||
|
||||
/* The following can't be #ifndef U_HIDE_INTERNAL_API, needed for other .h file declarations */
|
||||
/** @internal */
|
||||
struct ListFormatData : public UMemory {
|
||||
|
@ -131,21 +134,31 @@ class U_COMMON_API ListFormatter : public UObject{
|
|||
UnicodeString& appendTo, UErrorCode& errorCode) const;
|
||||
|
||||
#ifndef U_HIDE_INTERNAL_API
|
||||
/**
|
||||
@internal for MeasureFormat
|
||||
*/
|
||||
UnicodeString& format(
|
||||
const UnicodeString items[],
|
||||
int32_t n_items,
|
||||
UnicodeString& appendTo,
|
||||
int32_t index,
|
||||
int32_t &offset,
|
||||
UErrorCode& errorCode) const;
|
||||
/**
|
||||
* @internal constructor made public for testing.
|
||||
*/
|
||||
ListFormatter(const ListFormatData* listFormatterData);
|
||||
ListFormatter(const ListFormatData &data);
|
||||
ListFormatter(const ListFormatInternal* listFormatterInternal);
|
||||
#endif /* U_HIDE_INTERNAL_API */
|
||||
|
||||
private:
|
||||
static void initializeHash(UErrorCode& errorCode);
|
||||
static const ListFormatData* getListFormatData(const Locale& locale, const char *style, UErrorCode& errorCode);
|
||||
static const ListFormatInternal* getListFormatInternal(const Locale& locale, const char *style, UErrorCode& errorCode);
|
||||
|
||||
ListFormatter();
|
||||
void addNewString(const UnicodeString& pattern, UnicodeString& originalString,
|
||||
const UnicodeString& newString, UErrorCode& errorCode) const;
|
||||
|
||||
const ListFormatData* data;
|
||||
ListFormatInternal* owned;
|
||||
const ListFormatInternal* data;
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#******************************************************************************
|
||||
#
|
||||
# Copyright (C) 1998-2013, International Business Machines
|
||||
# Copyright (C) 1998-2014, International Business Machines
|
||||
# Corporation and others. All Rights Reserved.
|
||||
#
|
||||
#******************************************************************************
|
||||
|
@ -87,7 +87,7 @@ uspoof.o uspoof_impl.o uspoof_build.o uspoof_conf.o uspoof_wsconf.o decfmtst.o s
|
|||
ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o locdspnm.o \
|
||||
decNumber.o decContext.o alphaindex.o tznames.o tznames_impl.o tzgnames.o \
|
||||
tzfmt.o compactdecimalformat.o gender.o region.o scriptset.o identifier_info.o \
|
||||
uregion.o reldatefmt.o
|
||||
uregion.o reldatefmt.o quantityformatter.o
|
||||
|
||||
## Header files to install
|
||||
HEADERS = $(srcdir)/unicode/*.h
|
||||
|
|
162
icu4c/source/i18n/quantityformatter.cpp
Normal file
162
icu4c/source/i18n/quantityformatter.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
******************************************************************************
|
||||
* Copyright (C) 2014, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
******************************************************************************
|
||||
* quantityformatter.cpp
|
||||
*/
|
||||
#include "quantityformatter.h"
|
||||
#include "template.h"
|
||||
#include "uassert.h"
|
||||
#include "unicode/unistr.h"
|
||||
#include "unicode/decimfmt.h"
|
||||
#include "cstring.h"
|
||||
#include "plurrule_impl.h"
|
||||
#include "unicode/plurrule.h"
|
||||
#include "charstr.h"
|
||||
#include "unicode/fmtable.h"
|
||||
|
||||
#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
// other must always be first.
|
||||
static const char * const gPluralForms[] = {
|
||||
"other", "zero", "one", "two", "few", "many"};
|
||||
|
||||
static int32_t getPluralIndex(const char *pluralForm) {
|
||||
int32_t len = LENGTHOF(gPluralForms);
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
QuantityFormatter::QuantityFormatter() {
|
||||
for (int32_t i = 0; i < LENGTHOF(templates); ++i) {
|
||||
templates[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
QuantityFormatter::QuantityFormatter(const QuantityFormatter &other) {
|
||||
for (int32_t i = 0; i < LENGTHOF(templates); ++i) {
|
||||
if (other.templates[i] == NULL) {
|
||||
templates[i] = NULL;
|
||||
} else {
|
||||
templates[i] = new Template(*other.templates[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QuantityFormatter &QuantityFormatter::operator=(
|
||||
const QuantityFormatter& other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
for (int32_t i = 0; i < LENGTHOF(templates); ++i) {
|
||||
delete templates[i];
|
||||
if (other.templates[i] == NULL) {
|
||||
templates[i] = NULL;
|
||||
} else {
|
||||
templates[i] = new Template(*other.templates[i]);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
QuantityFormatter::~QuantityFormatter() {
|
||||
for (int32_t i = 0; i < LENGTHOF(templates); ++i) {
|
||||
delete templates[i];
|
||||
}
|
||||
}
|
||||
|
||||
void QuantityFormatter::reset() {
|
||||
for (int32_t i = 0; i < LENGTHOF(templates); ++i) {
|
||||
delete templates[i];
|
||||
templates[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
UBool QuantityFormatter::add(
|
||||
const char *variant,
|
||||
const UnicodeString &rawPattern,
|
||||
UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
int32_t pluralIndex = getPluralIndex(variant);
|
||||
if (pluralIndex == -1) {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return FALSE;
|
||||
}
|
||||
Template *newTemplate = new Template(rawPattern);
|
||||
if (newTemplate == NULL) {
|
||||
status = U_MEMORY_ALLOCATION_ERROR;
|
||||
return FALSE;
|
||||
}
|
||||
if (newTemplate->getPlaceholderCount() > 1) {
|
||||
delete newTemplate;
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return FALSE;
|
||||
}
|
||||
delete templates[pluralIndex];
|
||||
templates[pluralIndex] = newTemplate;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
UnicodeString &QuantityFormatter::format(
|
||||
const Formattable& quantity,
|
||||
const NumberFormat &fmt,
|
||||
const PluralRules &rules,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) const {
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
UnicodeString count;
|
||||
const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(&fmt);
|
||||
if (decFmt != NULL) {
|
||||
FixedDecimal fd = decFmt->getFixedDecimal(quantity, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
count = rules.select(fd);
|
||||
} else {
|
||||
if (quantity.getType() == Formattable::kDouble) {
|
||||
count = rules.select(quantity.getDouble());
|
||||
} else if (quantity.getType() == Formattable::kLong) {
|
||||
count = rules.select(quantity.getLong());
|
||||
} else if (quantity.getType() == Formattable::kInt64) {
|
||||
count = rules.select((double) quantity.getInt64());
|
||||
} else {
|
||||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
}
|
||||
CharString buffer;
|
||||
buffer.appendInvariantChars(count, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
int32_t pluralIndex = getPluralIndex(buffer.data());
|
||||
if (pluralIndex == -1) {
|
||||
pluralIndex = 0;
|
||||
}
|
||||
const Template *pattern = templates[pluralIndex];
|
||||
if (pattern == NULL) {
|
||||
pattern = templates[0];
|
||||
}
|
||||
if (pattern == NULL) {
|
||||
status = U_INVALID_STATE_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
UnicodeString formattedNumber;
|
||||
FieldPosition pos(0);
|
||||
fmt.format(quantity, formattedNumber, pos, status);
|
||||
UnicodeString *params[] = {&formattedNumber};
|
||||
return pattern->evaluate(params, LENGTHOF(params), appendTo, status);
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
102
icu4c/source/i18n/quantityformatter.h
Normal file
102
icu4c/source/i18n/quantityformatter.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
******************************************************************************
|
||||
* Copyright (C) 2014, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
******************************************************************************
|
||||
* quantityformatter.h
|
||||
*/
|
||||
|
||||
#ifndef __QUANTITY_FORMATTER_H__
|
||||
#define __QUANTITY_FORMATTER_H__
|
||||
|
||||
#include "unicode/utypes.h"
|
||||
#include "unicode/uobject.h"
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
class Template;
|
||||
class UnicodeString;
|
||||
class PluralRules;
|
||||
class NumberFormat;
|
||||
class Formattable;
|
||||
|
||||
/**
|
||||
* A plural aware template that is good for expressing a single quantity and
|
||||
* a unit.
|
||||
* <p>
|
||||
* First use the add() methods to add a pattern for each plural variant.
|
||||
* There must be a pattern for the "other" variant.
|
||||
* Then use the format() method.
|
||||
* <p>
|
||||
* Concurrent calls only to const methods on a QuantityFormatter object are
|
||||
* safe, but concurrent const and non-const method calls on a QuantityFormatter
|
||||
* object are not safe and require synchronization.
|
||||
*
|
||||
*/
|
||||
class U_COMMON_API QuantityFormatter : public UMemory {
|
||||
// TODO(Travis Keep): Add test for copy constructor, assignment, and reset.
|
||||
public:
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
QuantityFormatter();
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
QuantityFormatter(const QuantityFormatter& other);
|
||||
|
||||
/**
|
||||
* Assignment operator
|
||||
*/
|
||||
QuantityFormatter &operator=(const QuantityFormatter& other);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~QuantityFormatter();
|
||||
|
||||
/**
|
||||
* Removes all variants from this object including the "other" variant.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Adds a plural variant.
|
||||
*
|
||||
* @param variant "zero", "one", "two", "few", "many", "other"
|
||||
* @param rawPattern the pattern for the variant e.g "{0} meters"
|
||||
* @param status any error returned here.
|
||||
* @return TRUE on success; FALSE otherwise.
|
||||
*/
|
||||
UBool add(
|
||||
const char *variant,
|
||||
const UnicodeString &rawPattern,
|
||||
UErrorCode &status);
|
||||
|
||||
/**
|
||||
* Formats a quantity with this object appending the result to appendTo.
|
||||
* At least the "other" variant must be added to this object for this
|
||||
* method to work.
|
||||
*
|
||||
* @param quantity the single quantity.
|
||||
* @param fmt formats the quantity
|
||||
* @param rules computes the plural variant to use.
|
||||
* @param appendTo result appended here.
|
||||
* @param status any error returned here.
|
||||
* @return appendTo
|
||||
*/
|
||||
UnicodeString &format(
|
||||
const Formattable &quantity,
|
||||
const NumberFormat &fmt,
|
||||
const PluralRules &rules,
|
||||
UnicodeString &appendTo,
|
||||
UErrorCode &status) const;
|
||||
|
||||
private:
|
||||
Template *templates[6];
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
||||
#endif
|
|
@ -13,6 +13,7 @@
|
|||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
#include "unicode/localpointer.h"
|
||||
#include "quantityformatter.h"
|
||||
#include "unicode/plurrule.h"
|
||||
#include "unicode/msgfmt.h"
|
||||
#include "unicode/decimfmt.h"
|
||||
|
@ -48,12 +49,6 @@ U_CDECL_END
|
|||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
// other must always be first.
|
||||
static const char * const gPluralForms[] = {
|
||||
"other", "zero", "one", "two", "few", "many"};
|
||||
|
||||
static const UChar gPlaceholder[] = { 0x7b, 0x30, 0x7d}; /* {0} */
|
||||
|
||||
class QualitativeUnits : public UMemory {
|
||||
public:
|
||||
QualitativeUnits() { }
|
||||
|
@ -63,44 +58,10 @@ private:
|
|||
QualitativeUnits &operator=(const QualitativeUnits& other);
|
||||
};
|
||||
|
||||
struct UnitPattern {
|
||||
UnicodeString pattern;
|
||||
int32_t offset; // Offset of "{0}". -1 means no {0}.
|
||||
UBool valid; // True if initialize, false otherwise.
|
||||
|
||||
UnitPattern() : pattern(), offset(0), valid(FALSE) {
|
||||
}
|
||||
|
||||
void set(const UnicodeString &patternStr) {
|
||||
pattern = patternStr;
|
||||
offset = patternStr.indexOf(gPlaceholder, LENGTHOF(gPlaceholder), 0);
|
||||
valid = TRUE;
|
||||
}
|
||||
|
||||
UnicodeString& append(
|
||||
double quantity,
|
||||
const NumberFormat &nf,
|
||||
UnicodeString &appendTo) const {
|
||||
if (!valid) {
|
||||
return appendTo;
|
||||
}
|
||||
if (offset == -1) {
|
||||
appendTo.append(pattern);
|
||||
return appendTo;
|
||||
}
|
||||
appendTo.append(pattern, 0, offset);
|
||||
nf.format(quantity, appendTo);
|
||||
appendTo.append(pattern,
|
||||
offset + LENGTHOF(gPlaceholder),
|
||||
0x7fffffff);
|
||||
return appendTo;
|
||||
}
|
||||
};
|
||||
|
||||
class QuantitativeUnits : public UMemory {
|
||||
public:
|
||||
QuantitativeUnits() { }
|
||||
UnitPattern data[UDAT_RELATIVE_UNIT_COUNT][2][LENGTHOF(gPluralForms)];
|
||||
QuantityFormatter data[UDAT_RELATIVE_UNIT_COUNT][2];
|
||||
private:
|
||||
QuantitativeUnits(const QuantitativeUnits &other);
|
||||
QuantitativeUnits &operator=(const QuantitativeUnits& other);
|
||||
|
@ -172,21 +133,6 @@ static UBool getString(
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static UBool getUnitPattern(
|
||||
const UResourceBundle *resource,
|
||||
UnitPattern &result,
|
||||
UErrorCode &status) {
|
||||
if (U_FAILURE(status)) {
|
||||
return FALSE;
|
||||
}
|
||||
UnicodeString rawPattern;
|
||||
if (!getString(resource, rawPattern, status)) {
|
||||
return FALSE;
|
||||
}
|
||||
result.set(rawPattern);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UBool getStringByIndex(
|
||||
const UResourceBundle *resource,
|
||||
int32_t idx,
|
||||
|
@ -236,16 +182,6 @@ static void addQualitativeUnit(
|
|||
qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_PLAIN] = unitName;
|
||||
}
|
||||
|
||||
static int32_t getPluralIndex(const char *pluralForm) {
|
||||
int32_t len = LENGTHOF(gPluralForms);
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
if (uprv_strcmp(pluralForm, gPluralForms[i]) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void addTimeUnit(
|
||||
const UResourceBundle *resource,
|
||||
UDateRelativeUnit relativeUnit,
|
||||
|
@ -262,15 +198,16 @@ static void addTimeUnit(
|
|||
if (U_FAILURE(status)) {
|
||||
return;
|
||||
}
|
||||
int32_t pluralIndex = getPluralIndex(
|
||||
ures_getKey(pluralBundle.getAlias()));
|
||||
if (pluralIndex != -1) {
|
||||
if (!getUnitPattern(
|
||||
pluralBundle.getAlias(),
|
||||
quantitativeUnits.data[relativeUnit][pastOrFuture][pluralIndex],
|
||||
status)) {
|
||||
return;
|
||||
}
|
||||
UnicodeString rawPattern;
|
||||
if (!getString(pluralBundle.getAlias(), rawPattern, status)) {
|
||||
return;
|
||||
}
|
||||
if (!quantitativeUnits.data[relativeUnit][pastOrFuture]
|
||||
.add(
|
||||
ures_getKey(pluralBundle.getAlias()),
|
||||
rawPattern,
|
||||
status)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -710,28 +647,13 @@ UnicodeString& RelativeDateTimeFormatter::format(
|
|||
status = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
return appendTo;
|
||||
}
|
||||
FixedDecimal dec(quantity);
|
||||
const DecimalFormat *decFmt = dynamic_cast<const DecimalFormat *>(
|
||||
ptr->numberFormat.readOnly());
|
||||
if (decFmt != NULL) {
|
||||
dec = decFmt->getFixedDecimal(quantity, status);
|
||||
}
|
||||
CharString buffer;
|
||||
buffer.appendInvariantChars(ptr->pluralRules->select(dec), status);
|
||||
if (U_FAILURE(status)) {
|
||||
return appendTo;
|
||||
}
|
||||
|
||||
int32_t pluralIndex = getPluralIndex(buffer.data());
|
||||
if (pluralIndex == -1) {
|
||||
pluralIndex = 0;
|
||||
}
|
||||
int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
|
||||
const UnitPattern *pattern = &ptr->quantitativeUnits->data[unit][bFuture][pluralIndex];
|
||||
if (!pattern->valid) {
|
||||
pattern = &ptr->quantitativeUnits->data[unit][bFuture][0];
|
||||
}
|
||||
return pattern->append(quantity, *ptr->numberFormat, appendTo);
|
||||
return ptr->quantitativeUnits->data[unit][bFuture].format(
|
||||
quantity,
|
||||
*ptr->numberFormat,
|
||||
*ptr->pluralRules,
|
||||
appendTo,
|
||||
status);
|
||||
}
|
||||
|
||||
UnicodeString& RelativeDateTimeFormatter::format(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#******************************************************************************
|
||||
#
|
||||
# Copyright (C) 1999-2013, International Business Machines
|
||||
# Copyright (C) 1999-2014, International Business Machines
|
||||
# Corporation and others. All Rights Reserved.
|
||||
#
|
||||
#******************************************************************************
|
||||
|
@ -56,7 +56,7 @@ uobjtest.o idnaref.o idnaconf.o nptrans.o punyref.o testidn.o testidna.o uts46te
|
|||
incaltst.o calcasts.o v32test.o uvectest.o textfile.o tokiter.o utxttest.o \
|
||||
windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o tzoffloc.o tzfmttst.o ssearch.o dtifmtts.o \
|
||||
tufmtts.o itspoof.o simplethread.o bidiconf.o locnmtst.o dcfmtest.o alphaindextst.o listformattertest.o genderinfotest.o compactdecimalformattest.o regiontst.o \
|
||||
reldatefmttest.o lrucachetest.o
|
||||
reldatefmttest.o lrucachetest.o templatetest.o
|
||||
|
||||
DEPS = $(OBJECTS:.o=.d)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/********************************************************************
|
||||
* COPYRIGHT:
|
||||
* Copyright (c) 1997-2013, International Business Machines Corporation and
|
||||
* Copyright (c) 1997-2014, International Business Machines Corporation and
|
||||
* others. All Rights Reserved.
|
||||
********************************************************************/
|
||||
|
||||
|
@ -34,6 +34,7 @@ static IntlTest *createLocalPointerTest();
|
|||
extern IntlTest *createUCharsTrieTest();
|
||||
static IntlTest *createEnumSetTest();
|
||||
extern IntlTest *createLRUCacheTest();
|
||||
extern IntlTest *createTemplateTest();
|
||||
|
||||
#define CASE(id, test) case id: \
|
||||
name = #test; \
|
||||
|
@ -104,6 +105,14 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* &
|
|||
callTest(*test, par);
|
||||
}
|
||||
break;
|
||||
case 21:
|
||||
name = "TemplateTest";
|
||||
if (exec) {
|
||||
logln("TestSuite TemplateTest---"); logln();
|
||||
LocalPointer<IntlTest> test(createTemplateTest());
|
||||
callTest(*test, par);
|
||||
}
|
||||
break;
|
||||
default: name = ""; break; //needed to end loop
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012-2013, International Business Machines
|
||||
* Copyright (C) 2012-2014, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*
|
||||
*******************************************************************************
|
||||
|
@ -199,7 +199,7 @@ void ListFormatterTest::TestOutOfOrderPatterns() {
|
|||
|
||||
ListFormatData data("{1} after {0}", "{1} after the first {0}",
|
||||
"{1} after {0}", "{1} in the last after {0}");
|
||||
ListFormatter formatter(&data);
|
||||
ListFormatter formatter(data);
|
||||
|
||||
UnicodeString input1[] = {one};
|
||||
CheckFormatting(&formatter, input1, 1, results[0]);
|
||||
|
|
202
icu4c/source/test/intltest/templatetest.cpp
Normal file
202
icu4c/source/test/intltest/templatetest.cpp
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2014, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*
|
||||
* File TEMPLATETEST.CPP
|
||||
*
|
||||
********************************************************************************
|
||||
*/
|
||||
#include "cstring.h"
|
||||
#include "intltest.h"
|
||||
#include "template.h"
|
||||
|
||||
#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
class TemplateTest : public IntlTest {
|
||||
public:
|
||||
TemplateTest() {
|
||||
}
|
||||
void TestNoPlaceholders();
|
||||
void TestOnePlaceholder();
|
||||
void TestManyPlaceholders();
|
||||
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
|
||||
private:
|
||||
};
|
||||
|
||||
void TemplateTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* /*par*/) {
|
||||
TESTCASE_AUTO_BEGIN;
|
||||
TESTCASE_AUTO(TestNoPlaceholders);
|
||||
TESTCASE_AUTO(TestOnePlaceholder);
|
||||
TESTCASE_AUTO(TestManyPlaceholders);
|
||||
TESTCASE_AUTO_END;
|
||||
}
|
||||
|
||||
void TemplateTest::TestNoPlaceholders() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Template t("This doesn''t have templates '{0}");
|
||||
assertEquals("PlaceholderCount", 0, t.getPlaceholderCount());
|
||||
UnicodeString appendTo;
|
||||
assertEquals(
|
||||
"Evaluate",
|
||||
"This doesn't have templates {0}",
|
||||
t.evaluate(
|
||||
NULL,
|
||||
0,
|
||||
appendTo,
|
||||
status));
|
||||
appendTo.remove();
|
||||
t.compile("This has {} bad {012d placeholders", status);
|
||||
assertEquals("PlaceholderCount", 0, t.getPlaceholderCount());
|
||||
assertEquals(
|
||||
"Evaluate",
|
||||
"This has {} bad {012d placeholders",
|
||||
t.evaluate(
|
||||
NULL,
|
||||
0,
|
||||
appendTo,
|
||||
status));
|
||||
appendTo.remove();
|
||||
assertSuccess("Status", status);
|
||||
}
|
||||
|
||||
void TemplateTest::TestOnePlaceholder() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Template t;
|
||||
t.compile("{0} meter", status);
|
||||
assertEquals("PlaceholderCount", 1, t.getPlaceholderCount());
|
||||
UnicodeString one("1");
|
||||
UnicodeString *params[] = {&one};
|
||||
UnicodeString appendTo;
|
||||
assertEquals(
|
||||
"Evaluate",
|
||||
"1 meter",
|
||||
t.evaluate(
|
||||
params,
|
||||
LENGTHOF(params),
|
||||
appendTo,
|
||||
status));
|
||||
appendTo.remove();
|
||||
assertSuccess("Status", status);
|
||||
|
||||
// assignment
|
||||
Template s;
|
||||
s = t;
|
||||
assertEquals(
|
||||
"Assignment",
|
||||
"1 meter",
|
||||
s.evaluate(
|
||||
params,
|
||||
LENGTHOF(params),
|
||||
appendTo,
|
||||
status));
|
||||
appendTo.remove();
|
||||
|
||||
// Copy constructor
|
||||
Template r(t);
|
||||
assertEquals(
|
||||
"Copy constructor",
|
||||
"1 meter",
|
||||
r.evaluate(
|
||||
params,
|
||||
LENGTHOF(params),
|
||||
appendTo,
|
||||
status));
|
||||
appendTo.remove();
|
||||
assertSuccess("Status", status);
|
||||
}
|
||||
|
||||
void TemplateTest::TestManyPlaceholders() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
Template t;
|
||||
t.compile(
|
||||
"Templates {2}{1}{5} and {4} are out of order.", status);
|
||||
assertEquals("PlaceholderCount", 6, t.getPlaceholderCount());
|
||||
UnicodeString values[] = {
|
||||
"freddy", "tommy", "frog", "billy", "leg", "{0}"};
|
||||
UnicodeString *params[] = {
|
||||
&values[0], &values[1], &values[2], &values[3], &values[4], &values[5]};
|
||||
int32_t offsets[6];
|
||||
int32_t expectedOffsets[6] = {-1, 14, 10, -1, 27, 19};
|
||||
UnicodeString appendTo;
|
||||
assertEquals(
|
||||
"Evaluate",
|
||||
"Templates frogtommy{0} and leg are out of order.",
|
||||
t.evaluate(
|
||||
params,
|
||||
LENGTHOF(params),
|
||||
appendTo,
|
||||
offsets,
|
||||
LENGTHOF(offsets),
|
||||
status));
|
||||
appendTo.remove();
|
||||
assertSuccess("Status", status);
|
||||
for (int32_t i = 0; i < LENGTHOF(expectedOffsets); ++i) {
|
||||
if (expectedOffsets[i] != offsets[i]) {
|
||||
errln("Expected %d, got %d", expectedOffsets[i], offsets[i]);
|
||||
}
|
||||
}
|
||||
t.evaluate(
|
||||
params,
|
||||
LENGTHOF(params) - 1,
|
||||
appendTo,
|
||||
offsets,
|
||||
LENGTHOF(offsets),
|
||||
status);
|
||||
if (status != U_ILLEGAL_ARGUMENT_ERROR) {
|
||||
errln("Expected U_ILLEGAL_ARGUMENT_ERROR");
|
||||
}
|
||||
status = U_ZERO_ERROR;
|
||||
offsets[LENGTHOF(offsets) - 1] = 289;
|
||||
t.evaluate(
|
||||
params,
|
||||
LENGTHOF(params),
|
||||
appendTo,
|
||||
offsets,
|
||||
LENGTHOF(offsets) - 1,
|
||||
status);
|
||||
appendTo.remove();
|
||||
assertEquals("Offsets buffer length", 289, offsets[LENGTHOF(offsets) - 1]);
|
||||
|
||||
// Test assignment
|
||||
Template s;
|
||||
s = t;
|
||||
assertEquals(
|
||||
"Assignment",
|
||||
"Templates frogtommy{0} and leg are out of order.",
|
||||
s.evaluate(
|
||||
params,
|
||||
LENGTHOF(params),
|
||||
appendTo,
|
||||
status));
|
||||
appendTo.remove();
|
||||
|
||||
// Copy constructor
|
||||
Template r(t);
|
||||
assertEquals(
|
||||
"Assignment",
|
||||
"Templates frogtommy{0} and leg are out of order.",
|
||||
r.evaluate(
|
||||
params,
|
||||
LENGTHOF(params),
|
||||
appendTo,
|
||||
status));
|
||||
appendTo.remove();
|
||||
r.compile("{0} meter", status);
|
||||
assertEquals("PlaceholderCount", 1, r.getPlaceholderCount());
|
||||
assertEquals(
|
||||
"Assignment",
|
||||
"freddy meter",
|
||||
r.evaluate(
|
||||
params,
|
||||
1,
|
||||
appendTo,
|
||||
status));
|
||||
appendTo.remove();
|
||||
assertSuccess("Status", status);
|
||||
}
|
||||
|
||||
extern IntlTest *createTemplateTest() {
|
||||
return new TemplateTest();
|
||||
}
|
Loading…
Add table
Reference in a new issue