You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
442 lines
16 KiB
442 lines
16 KiB
// © 2016 and later: Unicode, Inc. and others.
|
|
// License & terms of use: http://www.unicode.org/copyright.html
|
|
/*
|
|
*******************************************************************************
|
|
* Copyright (C) 2009-2014, International Business Machines Corporation and
|
|
* others. All Rights Reserved.
|
|
*******************************************************************************
|
|
*/
|
|
|
|
#include "unicode/currpinf.h"
|
|
|
|
#if !UCONFIG_NO_FORMATTING
|
|
|
|
//#define CURRENCY_PLURAL_INFO_DEBUG 1
|
|
|
|
#ifdef CURRENCY_PLURAL_INFO_DEBUG
|
|
#include <iostream>
|
|
#endif
|
|
|
|
#include "unicode/locid.h"
|
|
#include "unicode/plurrule.h"
|
|
#include "unicode/strenum.h"
|
|
#include "unicode/ures.h"
|
|
#include "unicode/numsys.h"
|
|
#include "cstring.h"
|
|
#include "hash.h"
|
|
#include "uresimp.h"
|
|
#include "ureslocs.h"
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
static const UChar gNumberPatternSeparator = 0x3B; // ;
|
|
|
|
U_CDECL_BEGIN
|
|
|
|
/**
|
|
* @internal ICU 4.2
|
|
*/
|
|
static UBool U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2);
|
|
|
|
UBool
|
|
U_CALLCONV ValueComparator(UHashTok val1, UHashTok val2) {
|
|
const UnicodeString* affix_1 = (UnicodeString*)val1.pointer;
|
|
const UnicodeString* affix_2 = (UnicodeString*)val2.pointer;
|
|
return *affix_1 == *affix_2;
|
|
}
|
|
|
|
U_CDECL_END
|
|
|
|
|
|
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyPluralInfo)
|
|
|
|
static const UChar gDefaultCurrencyPluralPattern[] = {'0', '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4, 0};
|
|
static const UChar gTripleCurrencySign[] = {0xA4, 0xA4, 0xA4, 0};
|
|
static const UChar gPluralCountOther[] = {0x6F, 0x74, 0x68, 0x65, 0x72, 0};
|
|
static const UChar gPart0[] = {0x7B, 0x30, 0x7D, 0};
|
|
static const UChar gPart1[] = {0x7B, 0x31, 0x7D, 0};
|
|
|
|
static const char gNumberElementsTag[]="NumberElements";
|
|
static const char gLatnTag[]="latn";
|
|
static const char gPatternsTag[]="patterns";
|
|
static const char gDecimalFormatTag[]="decimalFormat";
|
|
static const char gCurrUnitPtnTag[]="CurrencyUnitPatterns";
|
|
|
|
CurrencyPluralInfo::CurrencyPluralInfo(UErrorCode& status)
|
|
: fPluralCountToCurrencyUnitPattern(nullptr),
|
|
fPluralRules(nullptr),
|
|
fLocale(nullptr),
|
|
fInternalStatus(U_ZERO_ERROR) {
|
|
initialize(Locale::getDefault(), status);
|
|
}
|
|
|
|
CurrencyPluralInfo::CurrencyPluralInfo(const Locale& locale, UErrorCode& status)
|
|
: fPluralCountToCurrencyUnitPattern(nullptr),
|
|
fPluralRules(nullptr),
|
|
fLocale(nullptr),
|
|
fInternalStatus(U_ZERO_ERROR) {
|
|
initialize(locale, status);
|
|
}
|
|
|
|
CurrencyPluralInfo::CurrencyPluralInfo(const CurrencyPluralInfo& info)
|
|
: UObject(info),
|
|
fPluralCountToCurrencyUnitPattern(nullptr),
|
|
fPluralRules(nullptr),
|
|
fLocale(nullptr),
|
|
fInternalStatus(U_ZERO_ERROR) {
|
|
*this = info;
|
|
}
|
|
|
|
CurrencyPluralInfo&
|
|
CurrencyPluralInfo::operator=(const CurrencyPluralInfo& info) {
|
|
if (this == &info) {
|
|
return *this;
|
|
}
|
|
|
|
fInternalStatus = info.fInternalStatus;
|
|
if (U_FAILURE(fInternalStatus)) {
|
|
// bail out early if the object we were copying from was already 'invalid'.
|
|
return *this;
|
|
}
|
|
|
|
deleteHash(fPluralCountToCurrencyUnitPattern);
|
|
fPluralCountToCurrencyUnitPattern = initHash(fInternalStatus);
|
|
copyHash(info.fPluralCountToCurrencyUnitPattern,
|
|
fPluralCountToCurrencyUnitPattern, fInternalStatus);
|
|
if ( U_FAILURE(fInternalStatus) ) {
|
|
return *this;
|
|
}
|
|
|
|
delete fPluralRules;
|
|
fPluralRules = nullptr;
|
|
delete fLocale;
|
|
fLocale = nullptr;
|
|
|
|
if (info.fPluralRules != nullptr) {
|
|
fPluralRules = info.fPluralRules->clone();
|
|
if (fPluralRules == nullptr) {
|
|
fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
|
|
return *this;
|
|
}
|
|
}
|
|
if (info.fLocale != nullptr) {
|
|
fLocale = info.fLocale->clone();
|
|
if (fLocale == nullptr) {
|
|
// Note: If clone had an error parameter, then we could check/set that instead.
|
|
fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
|
|
return *this;
|
|
}
|
|
// If the other locale wasn't bogus, but our clone'd locale is bogus, then OOM happened
|
|
// during the call to clone().
|
|
if (!info.fLocale->isBogus() && fLocale->isBogus()) {
|
|
fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
|
|
return *this;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
CurrencyPluralInfo::~CurrencyPluralInfo() {
|
|
deleteHash(fPluralCountToCurrencyUnitPattern);
|
|
fPluralCountToCurrencyUnitPattern = nullptr;
|
|
delete fPluralRules;
|
|
delete fLocale;
|
|
fPluralRules = nullptr;
|
|
fLocale = nullptr;
|
|
}
|
|
|
|
UBool
|
|
CurrencyPluralInfo::operator==(const CurrencyPluralInfo& info) const {
|
|
#ifdef CURRENCY_PLURAL_INFO_DEBUG
|
|
if (*fPluralRules == *info.fPluralRules) {
|
|
std::cout << "same plural rules\n";
|
|
}
|
|
if (*fLocale == *info.fLocale) {
|
|
std::cout << "same locale\n";
|
|
}
|
|
if (fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern)) {
|
|
std::cout << "same pattern\n";
|
|
}
|
|
#endif
|
|
return *fPluralRules == *info.fPluralRules &&
|
|
*fLocale == *info.fLocale &&
|
|
fPluralCountToCurrencyUnitPattern->equals(*info.fPluralCountToCurrencyUnitPattern);
|
|
}
|
|
|
|
|
|
CurrencyPluralInfo*
|
|
CurrencyPluralInfo::clone() const {
|
|
CurrencyPluralInfo* newObj = new CurrencyPluralInfo(*this);
|
|
// Since clone doesn't have a 'status' parameter, the best we can do is return nullptr
|
|
// if the new object was not full constructed properly (an error occurred).
|
|
if (newObj != nullptr && U_FAILURE(newObj->fInternalStatus)) {
|
|
delete newObj;
|
|
newObj = nullptr;
|
|
}
|
|
return newObj;
|
|
}
|
|
|
|
const PluralRules*
|
|
CurrencyPluralInfo::getPluralRules() const {
|
|
return fPluralRules;
|
|
}
|
|
|
|
UnicodeString&
|
|
CurrencyPluralInfo::getCurrencyPluralPattern(const UnicodeString& pluralCount,
|
|
UnicodeString& result) const {
|
|
const UnicodeString* currencyPluralPattern =
|
|
(UnicodeString*)fPluralCountToCurrencyUnitPattern->get(pluralCount);
|
|
if (currencyPluralPattern == nullptr) {
|
|
// fall back to "other"
|
|
if (pluralCount.compare(gPluralCountOther, 5)) {
|
|
currencyPluralPattern =
|
|
(UnicodeString*)fPluralCountToCurrencyUnitPattern->get(UnicodeString(TRUE, gPluralCountOther, 5));
|
|
}
|
|
if (currencyPluralPattern == nullptr) {
|
|
// no currencyUnitPatterns defined,
|
|
// fallback to predefined default.
|
|
// This should never happen when ICU resource files are
|
|
// available, since currencyUnitPattern of "other" is always
|
|
// defined in root.
|
|
result = UnicodeString(gDefaultCurrencyPluralPattern);
|
|
return result;
|
|
}
|
|
}
|
|
result = *currencyPluralPattern;
|
|
return result;
|
|
}
|
|
|
|
const Locale&
|
|
CurrencyPluralInfo::getLocale() const {
|
|
return *fLocale;
|
|
}
|
|
|
|
void
|
|
CurrencyPluralInfo::setPluralRules(const UnicodeString& ruleDescription,
|
|
UErrorCode& status) {
|
|
if (U_SUCCESS(status)) {
|
|
delete fPluralRules;
|
|
fPluralRules = PluralRules::createRules(ruleDescription, status);
|
|
}
|
|
}
|
|
|
|
void
|
|
CurrencyPluralInfo::setCurrencyPluralPattern(const UnicodeString& pluralCount,
|
|
const UnicodeString& pattern,
|
|
UErrorCode& status) {
|
|
if (U_SUCCESS(status)) {
|
|
UnicodeString* oldValue = static_cast<UnicodeString*>(
|
|
fPluralCountToCurrencyUnitPattern->get(pluralCount));
|
|
delete oldValue;
|
|
LocalPointer<UnicodeString> p(new UnicodeString(pattern), status);
|
|
if (U_SUCCESS(status)) {
|
|
// the p object allocated above will be owned by fPluralCountToCurrencyUnitPattern
|
|
// after the call to put(), even if the method returns failure.
|
|
fPluralCountToCurrencyUnitPattern->put(pluralCount, p.orphan(), status);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CurrencyPluralInfo::setLocale(const Locale& loc, UErrorCode& status) {
|
|
initialize(loc, status);
|
|
}
|
|
|
|
void
|
|
CurrencyPluralInfo::initialize(const Locale& loc, UErrorCode& status) {
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
delete fLocale;
|
|
fLocale = nullptr;
|
|
delete fPluralRules;
|
|
fPluralRules = nullptr;
|
|
|
|
fLocale = loc.clone();
|
|
if (fLocale == nullptr) {
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
// If the locale passed in wasn't bogus, but our clone'd locale is bogus, then OOM happened
|
|
// during the call to loc.clone().
|
|
if (!loc.isBogus() && fLocale->isBogus()) {
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
return;
|
|
}
|
|
fPluralRules = PluralRules::forLocale(loc, status);
|
|
setupCurrencyPluralPattern(loc, status);
|
|
}
|
|
|
|
void
|
|
CurrencyPluralInfo::setupCurrencyPluralPattern(const Locale& loc, UErrorCode& status) {
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
|
|
deleteHash(fPluralCountToCurrencyUnitPattern);
|
|
fPluralCountToCurrencyUnitPattern = initHash(status);
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
|
|
LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(loc, status), status);
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
UErrorCode ec = U_ZERO_ERROR;
|
|
LocalUResourceBundlePointer rb(ures_open(nullptr, loc.getName(), &ec));
|
|
LocalUResourceBundlePointer numElements(ures_getByKeyWithFallback(rb.getAlias(), gNumberElementsTag, nullptr, &ec));
|
|
ures_getByKeyWithFallback(numElements.getAlias(), ns->getName(), rb.getAlias(), &ec);
|
|
ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec);
|
|
int32_t ptnLen;
|
|
const UChar* numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec);
|
|
// Fall back to "latn" if num sys specific pattern isn't there.
|
|
if ( ec == U_MISSING_RESOURCE_ERROR && (uprv_strcmp(ns->getName(), gLatnTag) != 0)) {
|
|
ec = U_ZERO_ERROR;
|
|
ures_getByKeyWithFallback(numElements.getAlias(), gLatnTag, rb.getAlias(), &ec);
|
|
ures_getByKeyWithFallback(rb.getAlias(), gPatternsTag, rb.getAlias(), &ec);
|
|
numberStylePattern = ures_getStringByKeyWithFallback(rb.getAlias(), gDecimalFormatTag, &ptnLen, &ec);
|
|
}
|
|
int32_t numberStylePatternLen = ptnLen;
|
|
const UChar* negNumberStylePattern = nullptr;
|
|
int32_t negNumberStylePatternLen = 0;
|
|
// TODO: Java
|
|
// parse to check whether there is ";" separator in the numberStylePattern
|
|
UBool hasSeparator = false;
|
|
if (U_SUCCESS(ec)) {
|
|
for (int32_t styleCharIndex = 0; styleCharIndex < ptnLen; ++styleCharIndex) {
|
|
if (numberStylePattern[styleCharIndex] == gNumberPatternSeparator) {
|
|
hasSeparator = true;
|
|
// split the number style pattern into positive and negative
|
|
negNumberStylePattern = numberStylePattern + styleCharIndex + 1;
|
|
negNumberStylePatternLen = ptnLen - styleCharIndex - 1;
|
|
numberStylePatternLen = styleCharIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (U_FAILURE(ec)) {
|
|
// If OOM occurred during the above code, then we want to report that back to the caller.
|
|
if (ec == U_MEMORY_ALLOCATION_ERROR) {
|
|
status = ec;
|
|
}
|
|
return;
|
|
}
|
|
|
|
LocalUResourceBundlePointer currRb(ures_open(U_ICUDATA_CURR, loc.getName(), &ec));
|
|
LocalUResourceBundlePointer currencyRes(ures_getByKeyWithFallback(currRb.getAlias(), gCurrUnitPtnTag, nullptr, &ec));
|
|
|
|
#ifdef CURRENCY_PLURAL_INFO_DEBUG
|
|
std::cout << "in set up\n";
|
|
#endif
|
|
LocalPointer<StringEnumeration> keywords(fPluralRules->getKeywords(ec), ec);
|
|
if (U_SUCCESS(ec)) {
|
|
const char* pluralCount;
|
|
while (((pluralCount = keywords->next(nullptr, ec)) != nullptr) && U_SUCCESS(ec)) {
|
|
int32_t ptnLength;
|
|
UErrorCode err = U_ZERO_ERROR;
|
|
const UChar* patternChars = ures_getStringByKeyWithFallback(currencyRes.getAlias(), pluralCount, &ptnLength, &err);
|
|
if (err == U_MEMORY_ALLOCATION_ERROR || patternChars == nullptr) {
|
|
ec = err;
|
|
break;
|
|
}
|
|
if (U_SUCCESS(err) && ptnLength > 0) {
|
|
UnicodeString* pattern = new UnicodeString(patternChars, ptnLength);
|
|
if (pattern == nullptr) {
|
|
ec = U_MEMORY_ALLOCATION_ERROR;
|
|
break;
|
|
}
|
|
#ifdef CURRENCY_PLURAL_INFO_DEBUG
|
|
char result_1[1000];
|
|
pattern->extract(0, pattern->length(), result_1, "UTF-8");
|
|
std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
|
|
#endif
|
|
pattern->findAndReplace(UnicodeString(TRUE, gPart0, 3),
|
|
UnicodeString(numberStylePattern, numberStylePatternLen));
|
|
pattern->findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
|
|
|
|
if (hasSeparator) {
|
|
UnicodeString negPattern(patternChars, ptnLength);
|
|
negPattern.findAndReplace(UnicodeString(TRUE, gPart0, 3),
|
|
UnicodeString(negNumberStylePattern, negNumberStylePatternLen));
|
|
negPattern.findAndReplace(UnicodeString(TRUE, gPart1, 3), UnicodeString(TRUE, gTripleCurrencySign, 3));
|
|
pattern->append(gNumberPatternSeparator);
|
|
pattern->append(negPattern);
|
|
}
|
|
#ifdef CURRENCY_PLURAL_INFO_DEBUG
|
|
pattern->extract(0, pattern->length(), result_1, "UTF-8");
|
|
std::cout << "pluralCount: " << pluralCount << "; pattern: " << result_1 << "\n";
|
|
#endif
|
|
// the 'pattern' object allocated above will be owned by the fPluralCountToCurrencyUnitPattern after the call to
|
|
// put(), even if the method returns failure.
|
|
fPluralCountToCurrencyUnitPattern->put(UnicodeString(pluralCount, -1, US_INV), pattern, status);
|
|
}
|
|
}
|
|
}
|
|
// If OOM occurred during the above code, then we want to report that back to the caller.
|
|
if (ec == U_MEMORY_ALLOCATION_ERROR) {
|
|
status = ec;
|
|
}
|
|
}
|
|
|
|
void
|
|
CurrencyPluralInfo::deleteHash(Hashtable* hTable) {
|
|
if ( hTable == nullptr ) {
|
|
return;
|
|
}
|
|
int32_t pos = UHASH_FIRST;
|
|
const UHashElement* element = nullptr;
|
|
while ( (element = hTable->nextElement(pos)) != nullptr ) {
|
|
const UHashTok valueTok = element->value;
|
|
const UnicodeString* value = (UnicodeString*)valueTok.pointer;
|
|
delete value;
|
|
}
|
|
delete hTable;
|
|
hTable = nullptr;
|
|
}
|
|
|
|
Hashtable*
|
|
CurrencyPluralInfo::initHash(UErrorCode& status) {
|
|
if (U_FAILURE(status)) {
|
|
return nullptr;
|
|
}
|
|
LocalPointer<Hashtable> hTable(new Hashtable(TRUE, status), status);
|
|
if (U_FAILURE(status)) {
|
|
return nullptr;
|
|
}
|
|
hTable->setValueComparator(ValueComparator);
|
|
return hTable.orphan();
|
|
}
|
|
|
|
void
|
|
CurrencyPluralInfo::copyHash(const Hashtable* source,
|
|
Hashtable* target,
|
|
UErrorCode& status) {
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
int32_t pos = UHASH_FIRST;
|
|
const UHashElement* element = nullptr;
|
|
if (source) {
|
|
while ( (element = source->nextElement(pos)) != nullptr ) {
|
|
const UHashTok keyTok = element->key;
|
|
const UnicodeString* key = (UnicodeString*)keyTok.pointer;
|
|
const UHashTok valueTok = element->value;
|
|
const UnicodeString* value = (UnicodeString*)valueTok.pointer;
|
|
LocalPointer<UnicodeString> copy(new UnicodeString(*value), status);
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
// The HashTable owns the 'copy' object after the call to put().
|
|
target->put(UnicodeString(*key), copy.orphan(), status);
|
|
if (U_FAILURE(status)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
U_NAMESPACE_END
|
|
|
|
#endif
|