ICU-10461 Make NumberFormat::createInstance() and PluralRules::forLocale() 10X faster than before for most common types. Provide internal createSharedInstance() for NumberFormat and SharedFormat for internal use that is 2 orders of magnitude faster than before.

X-SVN-Rev: 35114
This commit is contained in:
Travis Keep 2014-02-10 19:13:50 +00:00
parent eb26b21238
commit 53ababf5a7
16 changed files with 735 additions and 338 deletions

2
.gitattributes vendored
View file

@ -80,6 +80,8 @@ icu4c/source/i18n/i18n.vcxproj.filters -text
icu4c/source/i18n/measunit.cpp -text
icu4c/source/i18n/quantityformatter.cpp -text
icu4c/source/i18n/quantityformatter.h -text
icu4c/source/i18n/sharednumberformat.h -text
icu4c/source/i18n/sharedpluralrules.h -text
icu4c/source/io/io.vcxproj -text
icu4c/source/io/io.vcxproj.filters -text
icu4c/source/layout/layout.vcxproj -text

View file

@ -1115,6 +1115,8 @@
</CustomBuild>
<ClInclude Include="plurrule_impl.h" />
<ClInclude Include="quantityformatter.h" />
<ClInclude Include="sharednumberformat.h" />
<ClInclude Include="sharedpluralrules.h" />
<CustomBuild Include="unicode\rbnf.h">
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">copy "%(FullPath)" ..\..\include\unicode
</Command>

View file

@ -644,6 +644,12 @@
<ClInclude Include="reldtfmt.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="sharednumberformat.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="sharedpluralrules.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="smpdtfst.h">
<Filter>formatting</Filter>
</ClInclude>

View file

@ -30,10 +30,12 @@
#include "unicode/putil.h"
#include "unicode/smpdtfmt.h"
#include "sharedptr.h"
#include "sharednumberformat.h"
#include "sharedpluralrules.h"
#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
#define MEAS_UNIT_COUNT 46
#define WIDTH_INDEX_COUNT (UMEASFMT_WIDTH_NARROW + 1)
static icu::LRUCache *gCache = NULL;
static UMutex gCacheMutex = U_MUTEX_INITIALIZER;
@ -52,20 +54,20 @@ U_CDECL_END
U_NAMESPACE_BEGIN
class UnitFormatters : public UMemory {
public:
UnitFormatters() { }
QuantityFormatter formatters[MEAS_UNIT_COUNT][UMEASFMT_WIDTH_NARROW + 1];
private:
UnitFormatters(const UnitFormatters &other);
UnitFormatters &operator=(const UnitFormatters &other);
};
// Used to format durations like 5:47 or 21:35:42.
class NumericDateFormatters : public UMemory {
public:
// Formats like H:mm
SimpleDateFormat hourMinute;
// formats like M:ss
SimpleDateFormat minuteSecond;
// formats like H:mm:ss
SimpleDateFormat hourMinuteSecond;
// Constructor that takes the actual patterns for hour-minute,
// minute-second, and hour-minute-second respectively.
NumericDateFormatters(
const UnicodeString &hm,
const UnicodeString &ms,
@ -84,24 +86,51 @@ private:
NumericDateFormatters &operator=(const NumericDateFormatters &other);
};
class MeasureFormatData : public SharedObject {
// Instances contain all MeasureFormat specific data for a particular locale.
// This data is cached. It is never copied, but is shared via shared pointers.
class MeasureFormatCacheData : public SharedObject {
public:
SharedPtr<UnitFormatters> unitFormatters;
SharedPtr<PluralRules> pluralRules;
SharedPtr<NumberFormat> numberFormat;
SharedPtr<NumberFormat> currencyFormats[UMEASFMT_WIDTH_NARROW + 1];
SharedPtr<NumericDateFormatters> numericDateFormatters;
virtual ~MeasureFormatData();
QuantityFormatter formatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT];
MeasureFormatCacheData();
void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) {
delete currencyFormats[widthIndex];
currencyFormats[widthIndex] = nfToAdopt;
}
const NumberFormat *getCurrencyFormat(int32_t widthIndex) const {
return currencyFormats[widthIndex];
}
void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) {
delete numericDateFormatters;
numericDateFormatters = formattersToAdopt;
}
const NumericDateFormatters *getNumericDateFormatters() const {
return numericDateFormatters;
}
virtual ~MeasureFormatCacheData();
private:
MeasureFormatData &operator=(const MeasureFormatData& other);
NumberFormat *currencyFormats[WIDTH_INDEX_COUNT];
NumericDateFormatters *numericDateFormatters;
MeasureFormatCacheData(const MeasureFormatCacheData &other);
MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other);
};
MeasureFormatData::~MeasureFormatData() {
MeasureFormatCacheData::MeasureFormatCacheData() {
for (int32_t i = 0; i < LENGTHOF(currencyFormats); ++i) {
currencyFormats[i] = NULL;
}
numericDateFormatters = NULL;
}
MeasureFormatCacheData::~MeasureFormatCacheData() {
for (int32_t i = 0; i < LENGTHOF(currencyFormats); ++i) {
delete currencyFormats[i];
}
delete numericDateFormatters;
}
static int32_t widthToIndex(UMeasureFormatWidth width) {
if (width > UMEASFMT_WIDTH_NARROW) {
return UMEASFMT_WIDTH_NARROW;
if (width >= WIDTH_INDEX_COUNT) {
return WIDTH_INDEX_COUNT - 1;
}
return width;
}
@ -124,9 +153,9 @@ static UBool getString(
}
static UBool load(
static UBool loadMeasureUnitData(
const UResourceBundle *resource,
UnitFormatters &unitFormatters,
MeasureFormatCacheData &cacheData,
UErrorCode &status) {
if (U_FAILURE(status)) {
return FALSE;
@ -144,7 +173,7 @@ static UBool load(
}
unitCount = MeasureUnit::getAvailable(units, unitCount, status);
}
for (int32_t currentWidth = 0; currentWidth <= UMEASFMT_WIDTH_NARROW; ++currentWidth) {
for (int32_t currentWidth = 0; currentWidth < WIDTH_INDEX_COUNT; ++currentWidth) {
// Be sure status is clear since next resource bundle lookup may fail.
if (U_FAILURE(status)) {
delete [] units;
@ -198,7 +227,7 @@ static UBool load(
}
UnicodeString rawPattern;
getString(pluralBundle.getAlias(), rawPattern, status);
unitFormatters.formatters[units[currentUnit].getIndex()][currentWidth].add(
cacheData.formatters[units[currentUnit].getIndex()][currentWidth].add(
ures_getKey(pluralBundle.getAlias()),
rawPattern,
status);
@ -260,6 +289,7 @@ static NumericDateFormatters *loadNumericDateFormatters(
return result;
}
// Creates the MeasureFormatCacheData for a particular locale
static SharedObject *U_CALLCONV createData(
const char *localeId, UErrorCode &status) {
LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
@ -268,66 +298,30 @@ static SharedObject *U_CALLCONV createData(
if (U_FAILURE(status)) {
return NULL;
}
LocalPointer<MeasureFormatData> result(new MeasureFormatData());
LocalPointer<UnitFormatters> unitFormatters(new UnitFormatters());
if (result.getAlias() == NULL
|| unitFormatters.getAlias() == NULL) {
LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData());
if (result.getAlias() == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
if (!load(
if (!loadMeasureUnitData(
topLevel.getAlias(),
*unitFormatters,
*result,
status)) {
return NULL;
}
if (!result->unitFormatters.reset(unitFormatters.orphan())) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
LocalPointer<NumericDateFormatters> ndf(
loadNumericDateFormatters(topLevel.getAlias(), status));
result->adoptNumericDateFormatters(loadNumericDateFormatters(
topLevel.getAlias(), status));
if (U_FAILURE(status)) {
return NULL;
}
if (!result->numericDateFormatters.reset(ndf.orphan())) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
LocalPointer<PluralRules> pr(PluralRules::forLocale(localeId, status));
if (U_FAILURE(status)) {
return NULL;
}
if (!result->pluralRules.reset(pr.orphan())) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
LocalPointer<NumberFormat> nf(
NumberFormat::createInstance(localeId, status));
if (U_FAILURE(status)) {
return NULL;
}
if (!result->numberFormat.reset(nf.orphan())) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
for (int32_t i = 0; i <= UMEASFMT_WIDTH_NARROW; ++i) {
LocalPointer<NumberFormat> cf(
NumberFormat::createInstance(
localeId, currencyStyles[i], status));
for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
result->adoptCurrencyFormat(i, NumberFormat::createInstance(
localeId, currencyStyles[i], status));
if (U_FAILURE(status)) {
return NULL;
}
if (!result->currencyFormats[i].reset(cf.orphan())) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
}
return result.orphan();
}
@ -342,16 +336,17 @@ static void U_CALLCONV cacheInit(UErrorCode &status) {
}
}
static void getFromCache(
static UBool getFromCache(
const char *locale,
const MeasureFormatData *&ptr,
const MeasureFormatCacheData *&ptr,
UErrorCode &status) {
umtx_initOnce(gCacheInitOnce, &cacheInit, status);
if (U_FAILURE(status)) {
return;
return FALSE;
}
Mutex lock(&gCacheMutex);
gCache->get(locale, ptr, status);
return U_SUCCESS(status);
}
static int32_t toHMS(
@ -405,8 +400,12 @@ static int32_t toHMS(
MeasureFormat::MeasureFormat(
const Locale &locale, UMeasureFormatWidth w, UErrorCode &status)
: ptr(NULL), width(w) {
initMeasureFormat(locale, w, status);
: cache(NULL),
numberFormat(NULL),
pluralRules(NULL),
width(w),
listFormatter(NULL) {
initMeasureFormat(locale, w, NULL, status);
}
MeasureFormat::MeasureFormat(
@ -414,14 +413,25 @@ MeasureFormat::MeasureFormat(
UMeasureFormatWidth w,
NumberFormat *nfToAdopt,
UErrorCode &status)
: ptr(NULL), width(w) {
initMeasureFormat(locale, w, status);
adoptNumberFormat(nfToAdopt, status);
: cache(NULL),
numberFormat(NULL),
pluralRules(NULL),
width(w),
listFormatter(NULL) {
initMeasureFormat(locale, w, nfToAdopt, status);
}
MeasureFormat::MeasureFormat(const MeasureFormat &other)
: Format(other), ptr(other.ptr), width(other.width) {
ptr->addRef();
MeasureFormat::MeasureFormat(const MeasureFormat &other) :
Format(other),
cache(other.cache),
numberFormat(other.numberFormat),
pluralRules(other.pluralRules),
width(other.width),
listFormatter(NULL) {
cache->addRef();
numberFormat->addRef();
pluralRules->addRef();
listFormatter = new ListFormatter(*other.listFormatter);
}
MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
@ -429,18 +439,34 @@ MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
return *this;
}
Format::operator=(other);
SharedObject::copyPtr(other.ptr, ptr);
SharedObject::copyPtr(other.cache, cache);
SharedObject::copyPtr(other.numberFormat, numberFormat);
SharedObject::copyPtr(other.pluralRules, pluralRules);
width = other.width;
delete listFormatter;
listFormatter = new ListFormatter(*other.listFormatter);
return *this;
}
MeasureFormat::MeasureFormat() : ptr(NULL), width(UMEASFMT_WIDTH_WIDE) {
MeasureFormat::MeasureFormat() :
cache(NULL),
numberFormat(NULL),
pluralRules(NULL),
width(UMEASFMT_WIDTH_WIDE),
listFormatter(NULL) {
}
MeasureFormat::~MeasureFormat() {
if (ptr != NULL) {
ptr->removeRef();
if (cache != NULL) {
cache->removeRef();
}
if (numberFormat != NULL) {
numberFormat->removeRef();
}
if (pluralRules != NULL) {
pluralRules->removeRef();
}
delete listFormatter;
}
UBool MeasureFormat::operator==(const Format &other) const {
@ -448,6 +474,10 @@ UBool MeasureFormat::operator==(const Format &other) const {
if (rhs == NULL) {
return FALSE;
}
// Note: Since the ListFormatter depends only on Locale and width, we
// don't have to check it here.
// Same objects are equivalent
if (this == rhs) {
return TRUE;
@ -456,21 +486,24 @@ UBool MeasureFormat::operator==(const Format &other) const {
if (width != rhs->width) {
return FALSE;
}
// Width the same, same shared data -> equivlanet
if (ptr == rhs->ptr) {
return TRUE;
// Width the same check locales.
// We don't need to check locales if both objects have same cache.
if (cache != rhs->cache) {
UErrorCode status = U_ZERO_ERROR;
const char *localeId = getLocaleID(status);
const char *rhsLocaleId = rhs->getLocaleID(status);
if (U_FAILURE(status)) {
// On failure, assume not equal
return FALSE;
}
if (uprv_strcmp(localeId, rhsLocaleId) != 0) {
return FALSE;
}
}
// Width same, but differing shred data. Depends on locale
// and number format objects being the same.
UErrorCode status = U_ZERO_ERROR;
const char *localeId = getLocaleID(status);
const char *rhsLocaleId = rhs->getLocaleID(status);
if (U_FAILURE(status)) {
// On failure, assume not equal
return FALSE;
}
return (uprv_strcmp(localeId, rhsLocaleId) == 0
&& *ptr->numberFormat == *rhs->ptr->numberFormat);
// Locales same, check NumberFormat if shared data differs.
return (
numberFormat == rhs->numberFormat ||
**numberFormat == **rhs->numberFormat);
}
Format *MeasureFormat::clone() const {
@ -507,7 +540,6 @@ UnicodeString &MeasureFormat::formatMeasures(
UnicodeString &appendTo,
FieldPosition &pos,
UErrorCode &status) const {
static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"};
if (U_FAILURE(status)) {
return appendTo;
}
@ -524,17 +556,9 @@ UnicodeString &MeasureFormat::formatMeasures(
return formatNumeric(hms, bitMap, appendTo, status);
}
}
LocalPointer<ListFormatter> lf(
ListFormatter::createInstance(
getLocale(status),
listStyles[widthToIndex(width)],
status));
if (U_FAILURE(status)) {
return appendTo;
}
if (pos.getField() != FieldPosition::DONT_CARE) {
return formatMeasuresSlowTrack(
measures, measureCount, *lf, appendTo, pos, status);
measures, measureCount, appendTo, pos, status);
}
UnicodeString *results = new UnicodeString[measureCount];
if (results == NULL) {
@ -544,51 +568,86 @@ UnicodeString &MeasureFormat::formatMeasures(
for (int32_t i = 0; i < measureCount; ++i) {
formatMeasure(measures[i], results[i], pos, status);
}
lf->format(results, measureCount, appendTo, status);
listFormatter->format(results, measureCount, appendTo, status);
delete [] results;
return appendTo;
}
void MeasureFormat::initMeasureFormat(
const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) {
const Locale &locale,
UMeasureFormatWidth w,
NumberFormat *nfToAdopt,
UErrorCode &status) {
static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"};
if (U_FAILURE(status)) {
return;
}
const char *name = locale.getName();
setLocaleIDs(name, name);
width = w;
getFromCache(name, ptr, status);
}
void MeasureFormat::adoptNumberFormat(NumberFormat *nfToAdopt, UErrorCode &status) {
if (!getFromCache(name, cache, status)) {
return;
}
SharedObject::copyPtr(
PluralRules::createSharedInstance(
locale, UPLURAL_TYPE_CARDINAL, status),
pluralRules);
if (U_FAILURE(status)) {
return;
}
MeasureFormatData* wptr = SharedObject::copyOnWrite(ptr);
if (wptr == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
pluralRules->removeRef();
if (nfToAdopt == NULL) {
SharedObject::copyPtr(
NumberFormat::createSharedInstance(
locale, UNUM_DECIMAL, status),
numberFormat);
if (U_FAILURE(status)) {
return;
}
numberFormat->removeRef();
} else {
adoptNumberFormat(nfToAdopt, status);
if (U_FAILURE(status)) {
return;
}
}
width = w;
delete listFormatter;
listFormatter = ListFormatter::createInstance(
locale,
listStyles[widthToIndex(width)],
status);
}
void MeasureFormat::adoptNumberFormat(
NumberFormat *nfToAdopt, UErrorCode &status) {
if (U_FAILURE(status)) {
return;
}
if (!wptr->numberFormat.reset(nfToAdopt)) {
SharedNumberFormat *shared = new SharedNumberFormat(nfToAdopt);
if (shared == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
delete nfToAdopt;
return;
}
SharedObject::copyPtr(shared, numberFormat);
}
UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) {
if (U_FAILURE(status) || locale == getLocale(status)) {
return FALSE;
}
initMeasureFormat(locale, width, status);
initMeasureFormat(locale, width, NULL, status);
return U_SUCCESS(status);
}
const NumberFormat &MeasureFormat::getNumberFormat() const {
return *ptr->numberFormat;
return **numberFormat;
}
const PluralRules &MeasureFormat::getPluralRules() const {
return *ptr->pluralRules;
return **pluralRules;
}
Locale MeasureFormat::getLocale(UErrorCode &status) const {
@ -612,7 +671,7 @@ UnicodeString &MeasureFormat::formatMeasure(
if (isCurrency(amtUnit)) {
UChar isoCode[4];
u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
return ptr->currencyFormats[widthToIndex(width)]->format(
return cache->getCurrencyFormat(widthToIndex(width))->format(
new CurrencyAmount(amtNumber, isoCode, status),
appendTo,
pos,
@ -625,8 +684,9 @@ UnicodeString &MeasureFormat::formatMeasure(
}
return quantityFormatter->format(
amtNumber,
*ptr->numberFormat,
*ptr->pluralRules, appendTo,
**numberFormat,
**pluralRules,
appendTo,
pos,
status);
}
@ -648,7 +708,7 @@ UnicodeString &MeasureFormat::formatNumeric(
case 7: // hms
return formatNumeric(
millis,
ptr->numericDateFormatters->hourMinuteSecond,
cache->getNumericDateFormatters()->hourMinuteSecond,
UDAT_SECOND_FIELD,
hms[2],
appendTo,
@ -657,7 +717,7 @@ UnicodeString &MeasureFormat::formatNumeric(
case 6: // ms
return formatNumeric(
millis,
ptr->numericDateFormatters->minuteSecond,
cache->getNumericDateFormatters()->minuteSecond,
UDAT_SECOND_FIELD,
hms[2],
appendTo,
@ -666,7 +726,7 @@ UnicodeString &MeasureFormat::formatNumeric(
case 3: // hm
return formatNumeric(
millis,
ptr->numericDateFormatters->hourMinute,
cache->getNumericDateFormatters()->hourMinute,
UDAT_MINUTE_FIELD,
hms[1],
appendTo,
@ -691,7 +751,7 @@ UnicodeString &MeasureFormat::formatNumeric(
return appendTo;
}
UnicodeString smallestAmountFormatted;
ptr->numberFormat->format(
(*numberFormat)->format(
smallestAmount, smallestAmountFormatted, status);
FieldPosition smallestFieldPosition(smallestField);
UnicodeString draft;
@ -718,7 +778,7 @@ const QuantityFormatter *MeasureFormat::getQuantityFormatter(
return NULL;
}
const QuantityFormatter *formatters =
ptr->unitFormatters->formatters[index];
cache->formatters[index];
if (formatters[widthIndex].isValid()) {
return &formatters[widthIndex];
}
@ -735,7 +795,6 @@ const QuantityFormatter *MeasureFormat::getQuantityFormatter(
UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
const Measure *measures,
int32_t measureCount,
const ListFormatter& lf,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const {
@ -761,7 +820,7 @@ UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
}
}
int32_t offset;
lf.format(
listFormatter->format(
results,
measureCount,
appendTo,

View file

@ -51,6 +51,8 @@
#include "mutex.h"
#include "digitlst.h"
#include <float.h>
#include "sharednumberformat.h"
#include "lrucache.h"
//#define FMT_DEBUG
@ -139,6 +141,10 @@ static const char *gFormatKeys[UNUM_FORMAT_STYLE_COUNT] = {
"currencyFormat" // UNUM_CURRENCY_PLURAL
};
static icu::LRUCache *gNumberFormatCache = NULL;
static UMutex gNumberFormatCacheMutex = U_MUTEX_INITIALIZER;
static icu::UInitOnce gNumberFormatCacheInitOnce = U_INITONCE_INITIALIZER;
// Static hashtable cache of NumberingSystem objects used by NumberFormat
static UHashtable * NumberingSystem_cache = NULL;
static UMutex nscacheMutex = U_MUTEX_INITIALIZER;
@ -172,7 +178,11 @@ static UBool U_CALLCONV numfmt_cleanup(void) {
uhash_close(NumberingSystem_cache);
NumberingSystem_cache = NULL;
}
gNumberFormatCacheInitOnce.reset();
if (gNumberFormatCache) {
delete gNumberFormatCache;
gNumberFormatCache = NULL;
}
return TRUE;
}
U_CDECL_END
@ -234,6 +244,10 @@ NumberFormat::~NumberFormat()
{
}
SharedNumberFormat::~SharedNumberFormat() {
delete ptr;
}
// -------------------------------------
// copy constructor
@ -1008,8 +1022,8 @@ NumberFormat::getAvailableLocales(void)
#endif /* UCONFIG_NO_SERVICE */
// -------------------------------------
NumberFormat* U_EXPORT2
NumberFormat::createInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) {
NumberFormat*
NumberFormat::internalCreateInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) {
#if !UCONFIG_NO_SERVICE
if (haveService()) {
return (NumberFormat*)gService->get(loc, kind, status);
@ -1018,6 +1032,23 @@ NumberFormat::createInstance(const Locale& loc, UNumberFormatStyle kind, UErrorC
return makeInstance(loc, kind, status);
}
NumberFormat* U_EXPORT2
NumberFormat::createInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) {
if (kind != UNUM_DECIMAL) {
return internalCreateInstance(loc, kind, status);
}
const SharedNumberFormat *shared = createSharedInstance(loc, kind, status);
if (U_FAILURE(status)) {
return NULL;
}
NumberFormat *result = (NumberFormat *) (*shared)->clone();
shared->removeRef();
if (result == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
}
return result;
}
// -------------------------------------
// Checks if the thousand/10 thousand grouping is used in the
@ -1205,6 +1236,61 @@ static void U_CALLCONV nscacheInit() {
uhash_setValueDeleter(NumberingSystem_cache, deleteNumberingSystem);
}
static SharedObject *U_CALLCONV createSharedNumberFormat(
const char *localeId, UErrorCode &status) {
if (U_FAILURE(status)) {
return NULL;
}
NumberFormat *nf = NumberFormat::internalCreateInstance(
localeId, UNUM_DECIMAL, status);
if (U_FAILURE(status)) {
return NULL;
}
SharedObject *result = new SharedNumberFormat(nf);
if (result == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
delete nf;
return NULL;
}
return result;
}
static void U_CALLCONV numberFormatCacheInit(UErrorCode &status) {
U_ASSERT(gNumberFormatCache == NULL);
ucln_i18n_registerCleanup(UCLN_I18N_NUMFMT, numfmt_cleanup);
gNumberFormatCache = new SimpleLRUCache(100, &createSharedNumberFormat, status);
if (U_FAILURE(status)) {
delete gNumberFormatCache;
gNumberFormatCache = NULL;
}
}
static void getSharedNumberFormatFromCache(
const char *locale,
const SharedNumberFormat *&ptr,
UErrorCode &status) {
umtx_initOnce(gNumberFormatCacheInitOnce, &numberFormatCacheInit, status);
if (U_FAILURE(status)) {
return;
}
Mutex lock(&gNumberFormatCacheMutex);
gNumberFormatCache->get(locale, ptr, status);
}
const SharedNumberFormat* U_EXPORT2
NumberFormat::createSharedInstance(const Locale& loc, UNumberFormatStyle kind, UErrorCode& status) {
if (U_FAILURE(status)) {
return NULL;
}
if (kind != UNUM_DECIMAL) {
status = U_UNSUPPORTED_ERROR;
return NULL;
}
const SharedNumberFormat *result = NULL;
getSharedNumberFormatFromCache(loc.getName(), result, status);
return result;
}
UBool
NumberFormat::isStyleSupported(UNumberFormatStyle style) {
return gLastResortNumberPatterns[style] != NULL;

View file

@ -29,9 +29,26 @@
#include "ustrfmt.h"
#include "uassert.h"
#include "uvectr32.h"
#include "sharedpluralrules.h"
#include "lrucache.h"
#if !UCONFIG_NO_FORMATTING
static icu::LRUCache *gPluralRulesCache = NULL;
static UMutex gPluralRulesCacheMutex = U_MUTEX_INITIALIZER;
static icu::UInitOnce gPluralRulesCacheInitOnce = U_INITONCE_INITIALIZER;
U_CDECL_BEGIN
static UBool U_CALLCONV plurrules_cleanup(void) {
gPluralRulesCacheInitOnce.reset();
if (gPluralRulesCache) {
delete gPluralRulesCache;
gPluralRulesCache = NULL;
}
return TRUE;
}
U_CDECL_END
U_NAMESPACE_BEGIN
#define ARRAY_SIZE(array) (int32_t)(sizeof array / sizeof array[0])
@ -73,6 +90,10 @@ PluralRules::~PluralRules() {
delete mRules;
}
SharedPluralRules::~SharedPluralRules() {
delete ptr;
}
PluralRules*
PluralRules::clone() const {
return new PluralRules(*this);
@ -131,6 +152,71 @@ PluralRules::createDefaultRules(UErrorCode& status) {
return createRules(UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1), status);
}
/******************************************************************************/
/* Create PluralRules cache */
static SharedObject *U_CALLCONV createSharedPluralRules(
const char *localeId, UErrorCode &status) {
if (U_FAILURE(status)) {
return NULL;
}
PluralRules *pr = PluralRules::internalForLocale(
localeId, UPLURAL_TYPE_CARDINAL, status);
if (U_FAILURE(status)) {
return NULL;
}
SharedObject *result = new SharedPluralRules(pr);
if (result == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
delete pr;
return NULL;
}
return result;
}
static void U_CALLCONV pluralRulesCacheInit(UErrorCode &status) {
U_ASSERT(gPluralRulesCache == NULL);
ucln_i18n_registerCleanup(UCLN_I18N_PLURAL_RULE, plurrules_cleanup);
gPluralRulesCache = new SimpleLRUCache(100, &createSharedPluralRules, status);
if (U_FAILURE(status)) {
delete gPluralRulesCache;
gPluralRulesCache = NULL;
}
}
static void getSharedPluralRulesFromCache(
const char *locale,
const SharedPluralRules *&ptr,
UErrorCode &status) {
umtx_initOnce(gPluralRulesCacheInitOnce, &pluralRulesCacheInit, status);
if (U_FAILURE(status)) {
return;
}
Mutex lock(&gPluralRulesCacheMutex);
gPluralRulesCache->get(locale, ptr, status);
}
/* end plural rules cache */
/******************************************************************************/
const SharedPluralRules* U_EXPORT2
PluralRules::createSharedInstance(
const Locale& locale, UPluralType type, UErrorCode& status) {
if (U_FAILURE(status)) {
return NULL;
}
if (type != UPLURAL_TYPE_CARDINAL) {
status = U_UNSUPPORTED_ERROR;
return NULL;
}
const SharedPluralRules *result = NULL;
getSharedPluralRulesFromCache(locale.getName(), result, status);
return result;
}
PluralRules* U_EXPORT2
PluralRules::forLocale(const Locale& locale, UErrorCode& status) {
return forLocale(locale, UPLURAL_TYPE_CARDINAL, status);
@ -138,6 +224,24 @@ PluralRules::forLocale(const Locale& locale, UErrorCode& status) {
PluralRules* U_EXPORT2
PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& status) {
if (type != UPLURAL_TYPE_CARDINAL) {
return internalForLocale(locale, type, status);
}
const SharedPluralRules *shared = createSharedInstance(
locale, type, status);
if (U_FAILURE(status)) {
return NULL;
}
PluralRules *result = (*shared)->clone();
shared->removeRef();
if (result == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
}
return result;
}
PluralRules* U_EXPORT2
PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status) {
if (U_FAILURE(status)) {
return NULL;
}

View file

@ -27,6 +27,8 @@
#include "charstr.h"
#include "sharedptr.h"
#include "sharedpluralrules.h"
#include "sharednumberformat.h"
// Copied from uscript_props.cpp
#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
@ -48,37 +50,35 @@ U_CDECL_END
U_NAMESPACE_BEGIN
class QualitativeUnits : public UMemory {
// RelativeDateTimeFormatter specific data for a single locale
class RelativeDateTimeCacheData: public SharedObject {
public:
QualitativeUnits() { }
UnicodeString data[UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
RelativeDateTimeCacheData() : combinedDateAndTime(NULL) { }
virtual ~RelativeDateTimeCacheData();
// no numbers: e.g Next Tuesday; Yesterday; etc.
UnicodeString absoluteUnits[UDAT_ABSOLUTE_UNIT_COUNT][UDAT_DIRECTION_COUNT];
// has numbers: e.g Next Tuesday; Yesterday; etc. For second index, 0
// means past e.g 5 days ago; 1 means future e.g in 5 days.
QuantityFormatter relativeUnits[UDAT_RELATIVE_UNIT_COUNT][2];
void adoptCombinedDateAndTime(MessageFormat *mfToAdopt) {
delete combinedDateAndTime;
combinedDateAndTime = mfToAdopt;
}
const MessageFormat *getCombinedDateAndTime() const {
return combinedDateAndTime;
}
private:
QualitativeUnits(const QualitativeUnits &other);
QualitativeUnits &operator=(const QualitativeUnits& other);
MessageFormat *combinedDateAndTime;
RelativeDateTimeCacheData(const RelativeDateTimeCacheData &other);
RelativeDateTimeCacheData& operator=(
const RelativeDateTimeCacheData &other);
};
class QuantitativeUnits : public UMemory {
public:
QuantitativeUnits() { }
QuantityFormatter data[UDAT_RELATIVE_UNIT_COUNT][2];
private:
QuantitativeUnits(const QuantitativeUnits &other);
QuantitativeUnits &operator=(const QuantitativeUnits& other);
};
class RelativeDateTimeData : public SharedObject {
public:
SharedPtr<QualitativeUnits> qualitativeUnits;
SharedPtr<QuantitativeUnits> quantitativeUnits;
SharedPtr<MessageFormat> combinedDateAndTime;
SharedPtr<PluralRules> pluralRules;
SharedPtr<NumberFormat> numberFormat;
virtual ~RelativeDateTimeData();
private:
RelativeDateTimeData &operator=(const RelativeDateTimeData& other);
};
RelativeDateTimeData::~RelativeDateTimeData() {
RelativeDateTimeCacheData::~RelativeDateTimeCacheData() {
delete combinedDateAndTime;
}
static UBool getStringWithFallback(
@ -147,45 +147,42 @@ static UBool getStringByIndex(
return TRUE;
}
static void addQualitativeUnit(
static void initAbsoluteUnit(
const UResourceBundle *resource,
UDateAbsoluteUnit absoluteUnit,
const UnicodeString &unitName,
QualitativeUnits &qualitativeUnits,
UnicodeString *absoluteUnit,
UErrorCode &status) {
getStringWithFallback(
resource,
"-1",
qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_LAST],
absoluteUnit[UDAT_DIRECTION_LAST],
status);
getStringWithFallback(
resource,
"0",
qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_THIS],
absoluteUnit[UDAT_DIRECTION_THIS],
status);
getStringWithFallback(
resource,
"1",
qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_NEXT],
absoluteUnit[UDAT_DIRECTION_NEXT],
status);
getOptionalStringWithFallback(
resource,
"-2",
qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_LAST_2],
absoluteUnit[UDAT_DIRECTION_LAST_2],
status);
getOptionalStringWithFallback(
resource,
"2",
qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_NEXT_2],
absoluteUnit[UDAT_DIRECTION_NEXT_2],
status);
qualitativeUnits.data[absoluteUnit][UDAT_DIRECTION_PLAIN] = unitName;
absoluteUnit[UDAT_DIRECTION_PLAIN] = unitName;
}
static void addTimeUnit(
static void initQuantityFormatter(
const UResourceBundle *resource,
UDateRelativeUnit relativeUnit,
int32_t pastOrFuture,
QuantitativeUnits &quantitativeUnits,
QuantityFormatter &formatter,
UErrorCode &status) {
if (U_FAILURE(status)) {
return;
@ -201,20 +198,18 @@ static void addTimeUnit(
if (!getString(pluralBundle.getAlias(), rawPattern, status)) {
return;
}
if (!quantitativeUnits.data[relativeUnit][pastOrFuture]
.add(
ures_getKey(pluralBundle.getAlias()),
rawPattern,
status)) {
if (!formatter.add(
ures_getKey(pluralBundle.getAlias()),
rawPattern,
status)) {
return;
}
}
}
static void addTimeUnit(
static void initRelativeUnit(
const UResourceBundle *resource,
UDateRelativeUnit relativeUnit,
QuantitativeUnits &quantitativeUnits,
QuantityFormatter *relativeUnit,
UErrorCode &status) {
LocalUResourceBundlePointer topLevel(
ures_getByKeyWithFallback(
@ -227,53 +222,46 @@ static void addTimeUnit(
if (U_FAILURE(status)) {
return;
}
addTimeUnit(
initQuantityFormatter(
futureBundle.getAlias(),
relativeUnit,
1,
quantitativeUnits,
relativeUnit[1],
status);
LocalUResourceBundlePointer pastBundle(ures_getByKeyWithFallback(
topLevel.getAlias(), "past", NULL, &status));
if (U_FAILURE(status)) {
return;
}
addTimeUnit(
initQuantityFormatter(
pastBundle.getAlias(),
relativeUnit,
0,
quantitativeUnits,
relativeUnit[0],
status);
}
static void addTimeUnit(
static void initRelativeUnit(
const UResourceBundle *resource,
const char *path,
UDateRelativeUnit relativeUnit,
QuantitativeUnits &quantitativeUnits,
QuantityFormatter *relativeUnit,
UErrorCode &status) {
LocalUResourceBundlePointer topLevel(
ures_getByKeyWithFallback(resource, path, NULL, &status));
if (U_FAILURE(status)) {
return;
}
addTimeUnit(topLevel.getAlias(), relativeUnit, quantitativeUnits, status);
initRelativeUnit(topLevel.getAlias(), relativeUnit, status);
}
static void addTimeUnit(
const UResourceBundle *resource,
const char *path,
UDateRelativeUnit relativeUnit,
UDateAbsoluteUnit absoluteUnit,
QuantitativeUnits &quantitativeUnits,
QualitativeUnits &qualitativeUnits,
QuantityFormatter *relativeUnit,
UnicodeString *absoluteUnit,
UErrorCode &status) {
LocalUResourceBundlePointer topLevel(
ures_getByKeyWithFallback(resource, path, NULL, &status));
if (U_FAILURE(status)) {
return;
}
addTimeUnit(topLevel.getAlias(), relativeUnit, quantitativeUnits, status);
initRelativeUnit(topLevel.getAlias(), relativeUnit, status);
UnicodeString unitName;
if (!getStringWithFallback(topLevel.getAlias(), "dn", unitName, status)) {
return;
@ -294,11 +282,10 @@ static void addTimeUnit(
if (U_FAILURE(status)) {
return;
}
addQualitativeUnit(
initAbsoluteUnit(
topLevel.getAlias(),
absoluteUnit,
unitName,
qualitativeUnits,
absoluteUnit,
status);
}
@ -329,80 +316,67 @@ static void addWeekDay(
const char *path,
const UnicodeString *daysOfWeek,
UDateAbsoluteUnit absoluteUnit,
QualitativeUnits &qualitativeUnits,
UnicodeString absoluteUnits[][UDAT_DIRECTION_COUNT],
UErrorCode &status) {
LocalUResourceBundlePointer topLevel(
ures_getByKeyWithFallback(resource, path, NULL, &status));
if (U_FAILURE(status)) {
return;
}
addQualitativeUnit(
initAbsoluteUnit(
topLevel.getAlias(),
absoluteUnit,
daysOfWeek[absoluteUnit - UDAT_ABSOLUTE_SUNDAY],
qualitativeUnits,
absoluteUnits[absoluteUnit],
status);
}
static UBool load(
static UBool loadUnitData(
const UResourceBundle *resource,
QualitativeUnits &qualitativeUnits,
QuantitativeUnits &quantitativeUnits,
RelativeDateTimeCacheData &cacheData,
UErrorCode &status) {
addTimeUnit(
resource,
"fields/day",
UDAT_RELATIVE_DAYS,
UDAT_ABSOLUTE_DAY,
quantitativeUnits,
qualitativeUnits,
cacheData.relativeUnits[UDAT_RELATIVE_DAYS],
cacheData.absoluteUnits[UDAT_ABSOLUTE_DAY],
status);
addTimeUnit(
resource,
"fields/week",
UDAT_RELATIVE_WEEKS,
UDAT_ABSOLUTE_WEEK,
quantitativeUnits,
qualitativeUnits,
cacheData.relativeUnits[UDAT_RELATIVE_WEEKS],
cacheData.absoluteUnits[UDAT_ABSOLUTE_WEEK],
status);
addTimeUnit(
resource,
"fields/month",
UDAT_RELATIVE_MONTHS,
UDAT_ABSOLUTE_MONTH,
quantitativeUnits,
qualitativeUnits,
cacheData.relativeUnits[UDAT_RELATIVE_MONTHS],
cacheData.absoluteUnits[UDAT_ABSOLUTE_MONTH],
status);
addTimeUnit(
resource,
"fields/year",
UDAT_RELATIVE_YEARS,
UDAT_ABSOLUTE_YEAR,
quantitativeUnits,
qualitativeUnits,
cacheData.relativeUnits[UDAT_RELATIVE_YEARS],
cacheData.absoluteUnits[UDAT_ABSOLUTE_YEAR],
status);
addTimeUnit(
initRelativeUnit(
resource,
"fields/second",
UDAT_RELATIVE_SECONDS,
quantitativeUnits,
cacheData.relativeUnits[UDAT_RELATIVE_SECONDS],
status);
addTimeUnit(
initRelativeUnit(
resource,
"fields/minute",
UDAT_RELATIVE_MINUTES,
quantitativeUnits,
cacheData.relativeUnits[UDAT_RELATIVE_MINUTES],
status);
addTimeUnit(
initRelativeUnit(
resource,
"fields/hour",
UDAT_RELATIVE_HOURS,
quantitativeUnits,
cacheData.relativeUnits[UDAT_RELATIVE_HOURS],
status);
getStringWithFallback(
resource,
"fields/second/relative/0",
qualitativeUnits.data[UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN],
cacheData.absoluteUnits[UDAT_ABSOLUTE_NOW][UDAT_DIRECTION_PLAIN],
status);
UnicodeString daysOfWeek[7];
readDaysOfWeek(
@ -415,49 +389,49 @@ static UBool load(
"fields/mon/relative",
daysOfWeek,
UDAT_ABSOLUTE_MONDAY,
qualitativeUnits,
cacheData.absoluteUnits,
status);
addWeekDay(
resource,
"fields/tue/relative",
daysOfWeek,
UDAT_ABSOLUTE_TUESDAY,
qualitativeUnits,
cacheData.absoluteUnits,
status);
addWeekDay(
resource,
"fields/wed/relative",
daysOfWeek,
UDAT_ABSOLUTE_WEDNESDAY,
qualitativeUnits,
cacheData.absoluteUnits,
status);
addWeekDay(
resource,
"fields/thu/relative",
daysOfWeek,
UDAT_ABSOLUTE_THURSDAY,
qualitativeUnits,
cacheData.absoluteUnits,
status);
addWeekDay(
resource,
"fields/fri/relative",
daysOfWeek,
UDAT_ABSOLUTE_FRIDAY,
qualitativeUnits,
cacheData.absoluteUnits,
status);
addWeekDay(
resource,
"fields/sat/relative",
daysOfWeek,
UDAT_ABSOLUTE_SATURDAY,
qualitativeUnits,
cacheData.absoluteUnits,
status);
addWeekDay(
resource,
"fields/sun/relative",
daysOfWeek,
UDAT_ABSOLUTE_SUNDAY,
qualitativeUnits,
cacheData.absoluteUnits,
status);
return U_SUCCESS(status);
}
@ -494,72 +468,34 @@ static UBool getDateTimePattern(
return getStringByIndex(topLevel.getAlias(), 8, result, status);
}
// Creates RelativeDateTimeFormatter specific data for a given locale
static SharedObject *U_CALLCONV createData(
const char *localeId, UErrorCode &status) {
LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
if (U_FAILURE(status)) {
return NULL;
}
LocalPointer<RelativeDateTimeData> result(new RelativeDateTimeData());
LocalPointer<QualitativeUnits> qualitativeUnits(new QualitativeUnits());
LocalPointer<QuantitativeUnits> quantitativeUnits(new QuantitativeUnits());
if (result.getAlias() == NULL
|| qualitativeUnits.getAlias() == NULL
|| quantitativeUnits.getAlias() == NULL) {
LocalPointer<RelativeDateTimeCacheData> result(
new RelativeDateTimeCacheData());
if (result.getAlias() == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
if (!load(
if (!loadUnitData(
topLevel.getAlias(),
*qualitativeUnits,
*quantitativeUnits,
*result,
status)) {
return NULL;
}
if (!result->qualitativeUnits.reset(qualitativeUnits.orphan())) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
if (!result->quantitativeUnits.reset(quantitativeUnits.orphan())) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
UnicodeString dateTimePattern;
if (!getDateTimePattern(topLevel.getAlias(), dateTimePattern, status)) {
return NULL;
}
LocalPointer<MessageFormat> mf(
result->adoptCombinedDateAndTime(
new MessageFormat(dateTimePattern, localeId, status));
if (U_FAILURE(status)) {
return NULL;
}
if (mf.getAlias() == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
if (!result->combinedDateAndTime.reset(mf.orphan())) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
LocalPointer<PluralRules> pr(PluralRules::forLocale(localeId, status));
if (U_FAILURE(status)) {
return NULL;
}
if (!result->pluralRules.reset(pr.orphan())) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
LocalPointer<NumberFormat> nf(
NumberFormat::createInstance(localeId, status));
if (U_FAILURE(status)) {
return NULL;
}
if (!result->numberFormat.reset(nf.orphan())) {
status = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
return result.orphan();
}
@ -573,67 +509,70 @@ static void U_CALLCONV cacheInit(UErrorCode &status) {
}
}
static void getFromCache(
static UBool getFromCache(
const char *locale,
const RelativeDateTimeData *&ptr,
const RelativeDateTimeCacheData *&ptr,
UErrorCode &status) {
umtx_initOnce(gCacheInitOnce, &cacheInit, status);
if (U_FAILURE(status)) {
return;
return FALSE;
}
Mutex lock(&gCacheMutex);
gCache->get(locale, ptr, status);
return U_SUCCESS(status);
}
RelativeDateTimeFormatter::RelativeDateTimeFormatter(UErrorCode& status)
: ptr(NULL) {
getFromCache(Locale::getDefault().getName(), ptr, status);
: cache(NULL), numberFormat(NULL), pluralRules(NULL) {
init(Locale::getDefault(), NULL, status);
}
RelativeDateTimeFormatter::RelativeDateTimeFormatter(
const Locale& locale, UErrorCode& status) : ptr(NULL) {
getFromCache(locale.getName(), ptr, status);
const Locale& locale, UErrorCode& status)
: cache(NULL), numberFormat(NULL), pluralRules(NULL) {
init(locale, NULL, status);
}
RelativeDateTimeFormatter::RelativeDateTimeFormatter(
const Locale& locale, NumberFormat *nfToAdopt, UErrorCode& status)
: ptr(NULL) {
getFromCache(locale.getName(), ptr, status);
if (U_FAILURE(status)) {
return;
}
RelativeDateTimeData* wptr = SharedObject::copyOnWrite(ptr);
if (wptr == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
if (!wptr->numberFormat.reset(nfToAdopt)) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
: cache(NULL), numberFormat(NULL), pluralRules(NULL) {
init(locale, nfToAdopt, status);
}
RelativeDateTimeFormatter::RelativeDateTimeFormatter(
const RelativeDateTimeFormatter& other) : ptr(other.ptr) {
ptr->addRef();
const RelativeDateTimeFormatter& other)
: cache(other.cache),
numberFormat(other.numberFormat),
pluralRules(other.pluralRules) {
cache->addRef();
numberFormat->addRef();
pluralRules->addRef();
}
RelativeDateTimeFormatter& RelativeDateTimeFormatter::operator=(
const RelativeDateTimeFormatter& other) {
if (this != &other) {
SharedObject::copyPtr(other.ptr, ptr);
SharedObject::copyPtr(other.cache, cache);
SharedObject::copyPtr(other.numberFormat, numberFormat);
SharedObject::copyPtr(other.pluralRules, pluralRules);
}
return *this;
}
RelativeDateTimeFormatter::~RelativeDateTimeFormatter() {
if (ptr != NULL) {
ptr->removeRef();
if (cache != NULL) {
cache->removeRef();
}
if (numberFormat != NULL) {
numberFormat->removeRef();
}
if (pluralRules != NULL) {
pluralRules->removeRef();
}
}
const NumberFormat& RelativeDateTimeFormatter::getNumberFormat() const {
return *ptr->numberFormat;
return **numberFormat;
}
UnicodeString& RelativeDateTimeFormatter::format(
@ -648,10 +587,10 @@ UnicodeString& RelativeDateTimeFormatter::format(
}
int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
FieldPosition pos(FieldPosition::DONT_CARE);
return ptr->quantitativeUnits->data[unit][bFuture].format(
return cache->relativeUnits[unit][bFuture].format(
quantity,
*ptr->numberFormat,
*ptr->pluralRules,
**numberFormat,
**pluralRules,
appendTo,
pos,
status);
@ -667,7 +606,7 @@ UnicodeString& RelativeDateTimeFormatter::format(
status = U_ILLEGAL_ARGUMENT_ERROR;
return appendTo;
}
return appendTo.append(ptr->qualitativeUnits->data[unit][direction]);
return appendTo.append(cache->absoluteUnits[unit][direction]);
}
UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
@ -675,9 +614,44 @@ UnicodeString& RelativeDateTimeFormatter::combineDateAndTime(
UnicodeString& appendTo, UErrorCode& status) const {
Formattable args[2] = {timeString, relativeDateString};
FieldPosition fpos(0);
return ptr->combinedDateAndTime->format(args, 2, appendTo, fpos, status);
return cache->getCombinedDateAndTime()->format(
args, 2, appendTo, fpos, status);
}
void RelativeDateTimeFormatter::init(
const Locale &locale, NumberFormat *nfToAdopt, UErrorCode &status) {
if (!getFromCache(locale.getName(), cache, status)) {
return;
}
SharedObject::copyPtr(
PluralRules::createSharedInstance(
locale, UPLURAL_TYPE_CARDINAL, status),
pluralRules);
if (U_FAILURE(status)) {
return;
}
pluralRules->removeRef();
if (nfToAdopt == NULL) {
SharedObject::copyPtr(
NumberFormat::createSharedInstance(
locale, UNUM_DECIMAL, status),
numberFormat);
if (U_FAILURE(status)) {
return;
}
numberFormat->removeRef();
} else {
SharedNumberFormat *shared = new SharedNumberFormat(nfToAdopt);
if (shared == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
delete nfToAdopt;
return;
}
SharedObject::copyPtr(shared, numberFormat);
}
}
U_NAMESPACE_END
#endif /* !UCONFIG_NO_FORMATTING */

View file

@ -0,0 +1,34 @@
/*
******************************************************************************
* Copyright (C) 2014, International Business Machines
* Corporation and others. All Rights Reserved.
******************************************************************************
* sharednumberformat.h
*/
#ifndef __SHARED_NUMBERFORMAT_H__
#define __SHARED_NUMBERFORMAT_H__
#include "unicode/utypes.h"
#include "sharedobject.h"
#include "sharedptr.h"
U_NAMESPACE_BEGIN
class NumberFormat;
class U_I18N_API SharedNumberFormat : public SharedObject {
public:
SharedNumberFormat(NumberFormat *nfToAdopt) : ptr(nfToAdopt) { }
virtual ~SharedNumberFormat();
const NumberFormat *operator->() const { return ptr; }
const NumberFormat &operator*() const { return *ptr; }
private:
NumberFormat *ptr;
SharedNumberFormat(const SharedNumberFormat &);
SharedNumberFormat &operator=(const SharedNumberFormat &);
};
U_NAMESPACE_END
#endif

View file

@ -0,0 +1,34 @@
/*
******************************************************************************
* Copyright (C) 2014, International Business Machines
* Corporation and others. All Rights Reserved.
******************************************************************************
* sharedpluralrules.h
*/
#ifndef __SHARED_PLURALRULES_H__
#define __SHARED_PLURALRULES_H__
#include "unicode/utypes.h"
#include "sharedobject.h"
#include "sharedptr.h"
U_NAMESPACE_BEGIN
class PluralRules;
class U_I18N_API SharedPluralRules : public SharedObject {
public:
SharedPluralRules(PluralRules *prToAdopt) : ptr(prToAdopt) { }
virtual ~SharedPluralRules();
const PluralRules *operator->() const { return ptr; }
const PluralRules &operator*() const { return *ptr; }
private:
PluralRules *ptr;
SharedPluralRules(const SharedPluralRules &);
SharedPluralRules &operator=(const SharedPluralRules &);
};
U_NAMESPACE_END
#endif

View file

@ -79,13 +79,13 @@ static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
TimeUnitFormat::TimeUnitFormat(UErrorCode& status) {
initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, status);
initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, NULL, status);
create(UTMUTFMT_FULL_STYLE, status);
}
TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, status);
initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
create(UTMUTFMT_FULL_STYLE, status);
}
@ -93,13 +93,13 @@ TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
switch (style) {
case UTMUTFMT_FULL_STYLE:
initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, status);
initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
break;
case UTMUTFMT_ABBREVIATED_STYLE:
initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, status);
initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, NULL, status);
break;
default:
initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, status);
initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, NULL, status);
break;
}
create(style, status);

View file

@ -72,7 +72,9 @@ U_NAMESPACE_BEGIN
class NumberFormat;
class PluralRules;
class MeasureFormatData;
class MeasureFormatCacheData;
class SharedNumberFormat;
class SharedPluralRules;
class QuantityFormatter;
class ListFormatter;
class DateFormat;
@ -208,10 +210,14 @@ class U_I18N_API MeasureFormat : public Format {
/**
* ICU use only.
* Initialize MeasureFormat class from base class.
* Initialize or change MeasureFormat class from subclass.
* @internal.
*/
void initMeasureFormat(const Locale &locale, UMeasureFormatWidth width, UErrorCode &status);
void initMeasureFormat(
const Locale &locale,
UMeasureFormatWidth width,
NumberFormat *nfToAdopt,
UErrorCode &status);
/**
* ICU use only.
@ -256,9 +262,16 @@ class U_I18N_API MeasureFormat : public Format {
#endif /* U_HIDE_INTERNAL_API */
private:
const MeasureFormatData *ptr;
const MeasureFormatCacheData *cache;
const SharedNumberFormat *numberFormat;
const SharedPluralRules *pluralRules;
UMeasureFormatWidth width;
// Declared outside of MeasureFormatSharedData because ListFormatter
// objects are relatively cheap to copy; therefore, they don't need to be
// shared across instances.
ListFormatter *listFormatter;
const QuantityFormatter *getQuantityFormatter(
int32_t index,
int32_t widthIndex,
@ -273,14 +286,14 @@ class U_I18N_API MeasureFormat : public Format {
UnicodeString &formatMeasuresSlowTrack(
const Measure *measures,
int32_t measureCount,
const ListFormatter& lf,
UnicodeString& appendTo,
FieldPosition& pos,
UErrorCode& status) const;
UnicodeString &formatNumeric(
const Formattable *hms, // always length 3
int32_t bitMap, // 1=hourset, 2=minuteset, 4=secondset
const Formattable *hms, // always length 3: [0] is hour; [1] is
// minute; [2] is second.
int32_t bitMap, // 1=hour set, 2=minute set, 4=second set
UnicodeString &appendTo,
UErrorCode &status) const;

View file

@ -43,6 +43,8 @@ class NumberFormatTest;
U_NAMESPACE_BEGIN
class SharedNumberFormat;
#if !UCONFIG_NO_SERVICE
class NumberFormatFactory;
class StringEnumeration;
@ -706,6 +708,30 @@ public:
UNumberFormatStyle style,
UErrorCode& errorCode);
#ifndef U_HIDE_INTERNAL_API
/**
* ICU use only.
* Creates NumberFormat instance without using the cache.
* @internal
*/
static NumberFormat* internalCreateInstance(
const Locale& desiredLocale,
UNumberFormatStyle style,
UErrorCode& errorCode);
/**
* ICU use only.
* Returns handle to the shared, cached NumberFormat instance for given
* locale. On success, caller must call removeRef() on returned value
* once it is done with the shared instance.
* @internal
*/
static const SharedNumberFormat* U_EXPORT2 createSharedInstance(
const Locale& inLocale, UNumberFormatStyle style, UErrorCode& status);
#endif
/**
* Returns a currency format for the current default locale.
* @stable ICU 2.0

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 2008-2013, International Business Machines Corporation and
* Copyright (C) 2008-2014, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
@ -43,6 +43,7 @@ class RuleChain;
class PluralRuleParser;
class PluralKeywordEnumeration;
class AndConstraint;
class SharedPluralRules;
/**
* Defines rules for mapping non-negative numeric values onto a small set of
@ -297,6 +298,25 @@ public:
* @internal
*/
static UBool hasOverride(const Locale &locale);
/**
* For ICU use only.
* creates a SharedPluralRules object
* @internal
*/
static PluralRules* U_EXPORT2 internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status);
/**
* For ICU use only.
* Returns handle to the shared, cached PluralRules isntance.
* Caller must call removeRef() on returned value once it is done with
* the shared instance.
* @internal
*/
static const SharedPluralRules* U_EXPORT2 createSharedInstance(
const Locale& locale, UPluralType type, UErrorCode& status);
#endif /* U_HIDE_INTERNAL_API */
/**

View file

@ -221,7 +221,9 @@ typedef enum UDateDirection {
U_NAMESPACE_BEGIN
class RelativeDateTimeData;
class RelativeDateTimeCacheData;
class SharedNumberFormat;
class SharedPluralRules;
class NumberFormat;
/**
@ -407,8 +409,10 @@ public:
const NumberFormat& getNumberFormat() const;
private:
RelativeDateTimeFormatter();
const RelativeDateTimeData* ptr;
const RelativeDateTimeCacheData* cache;
const SharedNumberFormat *numberFormat;
const SharedPluralRules *pluralRules;
void init(const Locale &, NumberFormat *nfToAdopt, UErrorCode &status);
};
U_NAMESPACE_END

View file

@ -52,6 +52,7 @@ private:
void TestFieldPositionMultiple();
void TestBadArg();
void TestEquality();
void TestBenchmark();
void verifyFormat(
const char *description,
const MeasureFormat &fmt,
@ -106,6 +107,7 @@ void MeasureFormatTest::runIndexedTest(
TESTCASE_AUTO(TestFieldPositionMultiple);
TESTCASE_AUTO(TestBadArg);
TESTCASE_AUTO(TestEquality);
TESTCASE_AUTO(TestBenchmark);
TESTCASE_AUTO_END;
}
@ -823,6 +825,29 @@ void MeasureFormatTest::TestEquality() {
assertTrue("Not Equal 3", fmt != fmtne3);
}
void MeasureFormatTest::TestBenchmark() {
/*
clock_t t;
UErrorCode status = U_ZERO_ERROR;
Locale en("en");
MeasureFormat fmt(en, UMEASFMT_WIDTH_SHORT, status);
MeasureFormat fmt2 = fmt;
Measure ms[] = {
Measure(70, MeasureUnit::createYear(status), status),
Measure(5, MeasureUnit::createMonth(status), status),
Measure(23, MeasureUnit::createDay(status), status),
Measure(15, MeasureUnit::createHour(status), status),
Measure(58, MeasureUnit::createMinute(status), status)};
FieldPosition pos(FieldPosition::DONT_CARE);
t = clock();
for (int32_t i = 0; i < 1000000; ++i) {
fmt2 = fmt;
}
t = clock() - t;
errln("It took %f seconds.", ((float)t)/CLOCKS_PER_SEC);
*/
}
void MeasureFormatTest::verifyFieldPosition(
const char *description,
const MeasureFormat &fmt,

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 2013, International Business Machines Corporation and *
* Copyright (C) 2013-2014, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
@ -294,7 +294,15 @@ void RelativeDateTimeFormatterTest::TestCustomNumberFormat() {
nf->setMinimumFractionDigits(1);
nf->setMaximumFractionDigits(1);
RelativeDateTimeFormatter fmt("en", nf, status);
RunTest(fmt, kEnglishDecimal, LENGTHOF(kEnglishDecimal), "en decimal digits");
// Test copy constructor.
RelativeDateTimeFormatter fmt2(fmt);
RunTest(fmt2, kEnglishDecimal, LENGTHOF(kEnglishDecimal), "en decimal digits");
// Test assignment
fmt = RelativeDateTimeFormatter("es", status);
RunTest(fmt, kSpanishNoQuantity, LENGTHOF(kSpanishNoQuantity), "assignment operator");
}
void RelativeDateTimeFormatterTest::TestCombineDateAndTime() {