2 *******************************************************************************
3 * Copyright (C) 1997-2014, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 * File COMPACTDECIMALFORMAT.CPP
9 ********************************************************************************
11 #include "unicode/utypes.h"
13 #if !UCONFIG_NO_FORMATTING
19 #include "unicode/compactdecimalformat.h"
20 #include "unicode/numsys.h"
21 #include "unicode/plurrule.h"
22 #include "unicode/ures.h"
26 #include "unicode/ures.h"
29 // Maps locale name to CDFLocaleData struct.
30 static UHashtable
* gCompactDecimalData
= NULL
;
31 static UMutex gCompactDecimalMetaLock
= U_MUTEX_INITIALIZER
;
35 static const int32_t MAX_DIGITS
= 15;
36 static const char gOther
[] = "other";
37 static const char gLatnTag
[] = "latn";
38 static const char gNumberElementsTag
[] = "NumberElements";
39 static const char gDecimalFormatTag
[] = "decimalFormat";
40 static const char gPatternsShort
[] = "patternsShort";
41 static const char gPatternsLong
[] = "patternsLong";
42 static const char gRoot
[] = "root";
44 static const UChar u_0
= 0x30;
45 static const UChar u_apos
= 0x27;
47 static const UChar kZero
[] = {u_0
};
49 // Used to unescape single quotes.
60 // Next one will be 4 then 6 etc.
64 // CDFUnit represents a prefix-suffix pair for a particular variant
66 struct CDFUnit
: public UMemory
{
69 inline CDFUnit() : prefix(), suffix() {
73 inline UBool
isSet() const {
74 return !prefix
.isBogus();
76 inline void markAsSet() {
81 // CDFLocaleStyleData contains formatting data for a particular locale
83 class CDFLocaleStyleData
: public UMemory
{
85 // What to divide by for each log10 value when formatting. These values
86 // will be powers of 10. For English, would be:
87 // 1, 1, 1, 1000, 1000, 1000, 1000000, 1000000, 1000000, 1000000000 ...
88 double divisors
[MAX_DIGITS
];
89 // Maps plural variants to CDFUnit[MAX_DIGITS] arrays.
90 // To format a number x,
91 // first compute log10(x). Compute displayNum = (x / divisors[log10(x)]).
92 // Compute the plural variant for displayNum
93 // (e.g zero, one, two, few, many, other).
94 // Compute cdfUnits = unitsByVariant[pluralVariant].
95 // Prefix and suffix to use at cdfUnits[log10(x)]
96 UHashtable
* unitsByVariant
;
97 inline CDFLocaleStyleData() : unitsByVariant(NULL
) {}
98 ~CDFLocaleStyleData();
99 // Init initializes this object.
100 void Init(UErrorCode
& status
);
101 inline UBool
isBogus() const {
102 return unitsByVariant
== NULL
;
106 CDFLocaleStyleData(const CDFLocaleStyleData
&);
107 CDFLocaleStyleData
& operator=(const CDFLocaleStyleData
&);
110 // CDFLocaleData contains formatting data for a particular locale.
111 struct CDFLocaleData
: public UMemory
{
112 CDFLocaleStyleData shortData
;
113 CDFLocaleStyleData longData
;
114 inline CDFLocaleData() : shortData(), longData() { }
115 inline ~CDFLocaleData() { }
116 // Init initializes this object.
117 void Init(UErrorCode
& status
);
124 static UBool U_CALLCONV
cdf_cleanup(void) {
125 if (gCompactDecimalData
!= NULL
) {
126 uhash_close(gCompactDecimalData
);
127 gCompactDecimalData
= NULL
;
132 static void U_CALLCONV
deleteCDFUnits(void* ptr
) {
133 delete [] (icu::CDFUnit
*) ptr
;
136 static void U_CALLCONV
deleteCDFLocaleData(void* ptr
) {
137 delete (icu::CDFLocaleData
*) ptr
;
144 static UBool
divisors_equal(const double* lhs
, const double* rhs
);
145 static const CDFLocaleStyleData
* getCDFLocaleStyleData(const Locale
& inLocale
, UNumberCompactStyle style
, UErrorCode
& status
);
147 static const CDFLocaleStyleData
* extractDataByStyleEnum(const CDFLocaleData
& data
, UNumberCompactStyle style
, UErrorCode
& status
);
148 static CDFLocaleData
* loadCDFLocaleData(const Locale
& inLocale
, UErrorCode
& status
);
149 static void initCDFLocaleData(const Locale
& inLocale
, CDFLocaleData
* result
, UErrorCode
& status
);
150 static UResourceBundle
* tryGetDecimalFallback(const UResourceBundle
* numberSystemResource
, const char* style
, UResourceBundle
** fillIn
, FallbackFlags flags
, UErrorCode
& status
);
151 static UResourceBundle
* tryGetByKeyWithFallback(const UResourceBundle
* rb
, const char* path
, UResourceBundle
** fillIn
, FallbackFlags flags
, UErrorCode
& status
);
152 static UBool
isRoot(const UResourceBundle
* rb
, UErrorCode
& status
);
153 static void initCDFLocaleStyleData(const UResourceBundle
* decimalFormatBundle
, CDFLocaleStyleData
* result
, UErrorCode
& status
);
154 static void populatePower10(const UResourceBundle
* power10Bundle
, CDFLocaleStyleData
* result
, UErrorCode
& status
);
155 static int32_t populatePrefixSuffix(const char* variant
, int32_t log10Value
, const UnicodeString
& formatStr
, UHashtable
* result
, UErrorCode
& status
);
156 static UBool
onlySpaces(UnicodeString u
);
157 static void fixQuotes(UnicodeString
& s
);
158 static void fillInMissing(CDFLocaleStyleData
* result
);
159 static int32_t computeLog10(double x
, UBool inRange
);
160 static CDFUnit
* createCDFUnit(const char* variant
, int32_t log10Value
, UHashtable
* table
, UErrorCode
& status
);
161 static const CDFUnit
* getCDFUnitFallback(const UHashtable
* table
, const UnicodeString
& variant
, int32_t log10Value
);
163 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompactDecimalFormat
)
165 CompactDecimalFormat::CompactDecimalFormat(
166 const DecimalFormat
& decimalFormat
,
167 const UHashtable
* unitsByVariant
,
168 const double* divisors
,
169 PluralRules
* pluralRules
)
170 : DecimalFormat(decimalFormat
), _unitsByVariant(unitsByVariant
), _divisors(divisors
), _pluralRules(pluralRules
) {
173 CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat
& source
)
174 : DecimalFormat(source
), _unitsByVariant(source
._unitsByVariant
), _divisors(source
._divisors
), _pluralRules(source
._pluralRules
->clone()) {
177 CompactDecimalFormat
* U_EXPORT2
178 CompactDecimalFormat::createInstance(
179 const Locale
& inLocale
, UNumberCompactStyle style
, UErrorCode
& status
) {
180 LocalPointer
<DecimalFormat
> decfmt((DecimalFormat
*) NumberFormat::makeInstance(inLocale
, UNUM_DECIMAL
, TRUE
, status
));
181 if (U_FAILURE(status
)) {
184 LocalPointer
<PluralRules
> pluralRules(PluralRules::forLocale(inLocale
, status
));
185 if (U_FAILURE(status
)) {
188 const CDFLocaleStyleData
* data
= getCDFLocaleStyleData(inLocale
, style
, status
);
189 if (U_FAILURE(status
)) {
192 CompactDecimalFormat
* result
=
193 new CompactDecimalFormat(*decfmt
, data
->unitsByVariant
, data
->divisors
, pluralRules
.getAlias());
194 if (result
== NULL
) {
195 status
= U_MEMORY_ALLOCATION_ERROR
;
198 pluralRules
.orphan();
199 result
->setMaximumSignificantDigits(3);
200 result
->setSignificantDigitsUsed(TRUE
);
201 result
->setGroupingUsed(FALSE
);
205 CompactDecimalFormat
&
206 CompactDecimalFormat::operator=(const CompactDecimalFormat
& rhs
) {
208 DecimalFormat::operator=(rhs
);
209 _unitsByVariant
= rhs
._unitsByVariant
;
210 _divisors
= rhs
._divisors
;
212 _pluralRules
= rhs
._pluralRules
->clone();
217 CompactDecimalFormat::~CompactDecimalFormat() {
223 CompactDecimalFormat::clone(void) const {
224 return new CompactDecimalFormat(*this);
228 CompactDecimalFormat::operator==(const Format
& that
) const {
232 return (DecimalFormat::operator==(that
) && eqHelper((const CompactDecimalFormat
&) that
));
236 CompactDecimalFormat::eqHelper(const CompactDecimalFormat
& that
) const {
237 return uhash_equals(_unitsByVariant
, that
._unitsByVariant
) && divisors_equal(_divisors
, that
._divisors
) && (*_pluralRules
== *that
._pluralRules
);
241 CompactDecimalFormat::format(
243 UnicodeString
& appendTo
,
244 FieldPosition
& pos
) const {
245 DigitList orig
, rounded
;
248 UErrorCode status
= U_ZERO_ERROR
;
249 _round(orig
, rounded
, isNegative
, status
);
250 if (U_FAILURE(status
)) {
253 double roundedDouble
= rounded
.getDouble();
255 roundedDouble
= -roundedDouble
;
257 int32_t baseIdx
= computeLog10(roundedDouble
, TRUE
);
258 double numberToFormat
= roundedDouble
/ _divisors
[baseIdx
];
259 UnicodeString variant
= _pluralRules
->select(numberToFormat
);
261 numberToFormat
= -numberToFormat
;
263 const CDFUnit
* unit
= getCDFUnitFallback(_unitsByVariant
, variant
, baseIdx
);
264 appendTo
+= unit
->prefix
;
265 DecimalFormat::format(numberToFormat
, appendTo
, pos
);
266 appendTo
+= unit
->suffix
;
271 CompactDecimalFormat::format(
273 UnicodeString
& appendTo
,
274 FieldPositionIterator
* /* posIter */,
275 UErrorCode
& status
) const {
276 status
= U_UNSUPPORTED_ERROR
;
281 CompactDecimalFormat::format(
283 UnicodeString
& appendTo
,
284 FieldPosition
& pos
) const {
285 return format((double) number
, appendTo
, pos
);
289 CompactDecimalFormat::format(
290 int64_t /* number */,
291 UnicodeString
& appendTo
,
292 FieldPositionIterator
* /* posIter */,
293 UErrorCode
& status
) const {
294 status
= U_UNSUPPORTED_ERROR
;
299 CompactDecimalFormat::format(
300 const StringPiece
& /* number */,
301 UnicodeString
& appendTo
,
302 FieldPositionIterator
* /* posIter */,
303 UErrorCode
& status
) const {
304 status
= U_UNSUPPORTED_ERROR
;
309 CompactDecimalFormat::format(
310 const DigitList
& /* number */,
311 UnicodeString
& appendTo
,
312 FieldPositionIterator
* /* posIter */,
313 UErrorCode
& status
) const {
314 status
= U_UNSUPPORTED_ERROR
;
319 CompactDecimalFormat::format(const DigitList
& /* number */,
320 UnicodeString
& appendTo
,
321 FieldPosition
& /* pos */,
322 UErrorCode
& status
) const {
323 status
= U_UNSUPPORTED_ERROR
;
328 CompactDecimalFormat::parse(
329 const UnicodeString
& /* text */,
330 Formattable
& /* result */,
331 ParsePosition
& /* parsePosition */) const {
335 CompactDecimalFormat::parse(
336 const UnicodeString
& /* text */,
337 Formattable
& /* result */,
338 UErrorCode
& status
) const {
339 status
= U_UNSUPPORTED_ERROR
;
343 CompactDecimalFormat::parseCurrency(
344 const UnicodeString
& /* text */,
345 ParsePosition
& /* pos */) const {
349 void CDFLocaleStyleData::Init(UErrorCode
& status
) {
350 if (unitsByVariant
!= NULL
) {
353 unitsByVariant
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
354 if (U_FAILURE(status
)) {
357 uhash_setKeyDeleter(unitsByVariant
, uprv_free
);
358 uhash_setValueDeleter(unitsByVariant
, deleteCDFUnits
);
361 CDFLocaleStyleData::~CDFLocaleStyleData() {
365 void CDFLocaleStyleData::setToBogus() {
366 if (unitsByVariant
!= NULL
) {
367 uhash_close(unitsByVariant
);
368 unitsByVariant
= NULL
;
372 void CDFLocaleData::Init(UErrorCode
& status
) {
373 shortData
.Init(status
);
374 if (U_FAILURE(status
)) {
377 longData
.Init(status
);
380 // Helper method for operator=
381 static UBool
divisors_equal(const double* lhs
, const double* rhs
) {
382 for (int32_t i
= 0; i
< MAX_DIGITS
; ++i
) {
383 if (lhs
[i
] != rhs
[i
]) {
390 // getCDFLocaleStyleData returns pointer to formatting data for given locale and
391 // style within the global cache. On cache miss, getCDFLocaleStyleData loads
392 // the data from CLDR into the global cache before returning the pointer. If a
393 // UNUM_LONG data is requested for a locale, and that locale does not have
394 // UNUM_LONG data, getCDFLocaleStyleData will fall back to UNUM_SHORT data for
396 static const CDFLocaleStyleData
* getCDFLocaleStyleData(const Locale
& inLocale
, UNumberCompactStyle style
, UErrorCode
& status
) {
397 if (U_FAILURE(status
)) {
400 CDFLocaleData
* result
= NULL
;
401 const char* key
= inLocale
.getName();
403 Mutex
lock(&gCompactDecimalMetaLock
);
404 if (gCompactDecimalData
== NULL
) {
405 gCompactDecimalData
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
406 if (U_FAILURE(status
)) {
409 uhash_setKeyDeleter(gCompactDecimalData
, uprv_free
);
410 uhash_setValueDeleter(gCompactDecimalData
, deleteCDFLocaleData
);
411 ucln_i18n_registerCleanup(UCLN_I18N_CDFINFO
, cdf_cleanup
);
413 result
= (CDFLocaleData
*) uhash_get(gCompactDecimalData
, key
);
416 if (result
!= NULL
) {
417 return extractDataByStyleEnum(*result
, style
, status
);
420 result
= loadCDFLocaleData(inLocale
, status
);
421 if (U_FAILURE(status
)) {
426 Mutex
lock(&gCompactDecimalMetaLock
);
427 CDFLocaleData
* temp
= (CDFLocaleData
*) uhash_get(gCompactDecimalData
, key
);
432 uhash_put(gCompactDecimalData
, uprv_strdup(key
), (void*) result
, &status
);
433 if (U_FAILURE(status
)) {
438 return extractDataByStyleEnum(*result
, style
, status
);
441 static const CDFLocaleStyleData
* extractDataByStyleEnum(const CDFLocaleData
& data
, UNumberCompactStyle style
, UErrorCode
& status
) {
444 return &data
.shortData
;
446 if (!data
.longData
.isBogus()) {
447 return &data
.longData
;
449 return &data
.shortData
;
451 status
= U_ILLEGAL_ARGUMENT_ERROR
;
456 // loadCDFLocaleData loads formatting data from CLDR for a given locale. The
457 // caller owns the returned pointer.
458 static CDFLocaleData
* loadCDFLocaleData(const Locale
& inLocale
, UErrorCode
& status
) {
459 if (U_FAILURE(status
)) {
462 CDFLocaleData
* result
= new CDFLocaleData
;
463 if (result
== NULL
) {
464 status
= U_MEMORY_ALLOCATION_ERROR
;
467 result
->Init(status
);
468 if (U_FAILURE(status
)) {
473 initCDFLocaleData(inLocale
, result
, status
);
474 if (U_FAILURE(status
)) {
481 // initCDFLocaleData initializes result with data from CLDR.
482 // inLocale is the locale, the CLDR data is stored in result.
483 // We load the UNUM_SHORT and UNUM_LONG data looking first in local numbering
484 // system and not including root locale in fallback. Next we try in the latn
485 // numbering system where we fallback all the way to root. If we don't find
486 // UNUM_SHORT data in these three places, we report an error. If we find
487 // UNUM_SHORT data before finding UNUM_LONG data we make UNUM_LONG data fall
488 // back to UNUM_SHORT data.
489 static void initCDFLocaleData(const Locale
& inLocale
, CDFLocaleData
* result
, UErrorCode
& status
) {
490 LocalPointer
<NumberingSystem
> ns(NumberingSystem::createInstance(inLocale
, status
));
491 if (U_FAILURE(status
)) {
494 const char* numberingSystemName
= ns
->getName();
495 UResourceBundle
* rb
= ures_open(NULL
, inLocale
.getName(), &status
);
496 rb
= ures_getByKeyWithFallback(rb
, gNumberElementsTag
, rb
, &status
);
497 if (U_FAILURE(status
)) {
501 UResourceBundle
* shortDataFillIn
= NULL
;
502 UResourceBundle
* longDataFillIn
= NULL
;
503 UResourceBundle
* shortData
= NULL
;
504 UResourceBundle
* longData
= NULL
;
506 if (uprv_strcmp(numberingSystemName
, gLatnTag
) != 0) {
507 LocalUResourceBundlePointer
localResource(
508 tryGetByKeyWithFallback(rb
, numberingSystemName
, NULL
, NOT_ROOT
, status
));
509 shortData
= tryGetDecimalFallback(
510 localResource
.getAlias(), gPatternsShort
, &shortDataFillIn
, NOT_ROOT
, status
);
511 longData
= tryGetDecimalFallback(
512 localResource
.getAlias(), gPatternsLong
, &longDataFillIn
, NOT_ROOT
, status
);
514 if (U_FAILURE(status
)) {
515 ures_close(shortDataFillIn
);
516 ures_close(longDataFillIn
);
521 // If we haven't found UNUM_SHORT look in latn numbering system. We must
522 // succeed at finding UNUM_SHORT here.
523 if (shortData
== NULL
) {
524 LocalUResourceBundlePointer
latnResource(tryGetByKeyWithFallback(rb
, gLatnTag
, NULL
, MUST
, status
));
525 shortData
= tryGetDecimalFallback(latnResource
.getAlias(), gPatternsShort
, &shortDataFillIn
, MUST
, status
);
526 if (longData
== NULL
) {
527 longData
= tryGetDecimalFallback(latnResource
.getAlias(), gPatternsLong
, &longDataFillIn
, ANY
, status
);
528 if (longData
!= NULL
&& isRoot(longData
, status
) && !isRoot(shortData
, status
)) {
533 initCDFLocaleStyleData(shortData
, &result
->shortData
, status
);
534 ures_close(shortDataFillIn
);
535 if (U_FAILURE(status
)) {
536 ures_close(longDataFillIn
);
540 if (longData
== NULL
) {
541 result
->longData
.setToBogus();
543 initCDFLocaleStyleData(longData
, &result
->longData
, status
);
545 ures_close(longDataFillIn
);
550 * tryGetDecimalFallback attempts to fetch the "decimalFormat" resource bundle
551 * with a particular style. style is either "patternsShort" or "patternsLong."
552 * FillIn, flags, and status work in the same way as in tryGetByKeyWithFallback.
554 static UResourceBundle
* tryGetDecimalFallback(const UResourceBundle
* numberSystemResource
, const char* style
, UResourceBundle
** fillIn
, FallbackFlags flags
, UErrorCode
& status
) {
555 UResourceBundle
* first
= tryGetByKeyWithFallback(numberSystemResource
, style
, fillIn
, flags
, status
);
556 UResourceBundle
* second
= tryGetByKeyWithFallback(first
, gDecimalFormatTag
, fillIn
, flags
, status
);
557 if (fillIn
== NULL
) {
563 // tryGetByKeyWithFallback returns a sub-resource bundle that matches given
564 // criteria or NULL if none found. rb is the resource bundle that we are
565 // searching. If rb == NULL then this function behaves as if no sub-resource
566 // is found; path is the key of the sub-resource,
567 // (i.e "foo" but not "foo/bar"); If fillIn is NULL, caller must always call
568 // ures_close() on returned resource. See below for example when fillIn is
569 // not NULL. flags is ANY or NOT_ROOT. Optionally, these values
570 // can be ored with MUST. MUST by itself is the same as ANY | MUST.
571 // The locale of the returned sub-resource will either match the
572 // flags or the returned sub-resouce will be NULL. If MUST is included in
573 // flags, and not suitable sub-resource is found then in addition to returning
574 // NULL, this function also sets status to U_MISSING_RESOURCE_ERROR. If MUST
575 // is not included in flags, then this function just returns NULL if no
576 // such sub-resource is found and will never set status to
577 // U_MISSING_RESOURCE_ERROR.
579 // Example: This code first searches for "foo/bar" sub-resource without falling
580 // back to ROOT. Then searches for "baz" sub-resource as last resort.
582 // UResourcebundle* fillIn = NULL;
583 // UResourceBundle* data = tryGetByKeyWithFallback(rb, "foo", &fillIn, NON_ROOT, status);
584 // data = tryGetByKeyWithFallback(data, "bar", &fillIn, NON_ROOT, status);
586 // data = tryGetbyKeyWithFallback(rb, "baz", &fillIn, MUST, status);
588 // if (U_FAILURE(status)) {
589 // ures_close(fillIn);
592 // doStuffWithNonNullSubresource(data);
594 // /* Wrong! don't do the following as it can leak memory if fillIn gets set
596 // fillIn = tryGetByKeyWithFallback(rb, "wrong", &fillIn, ANY, status);
598 // ures_close(fillIn);
600 static UResourceBundle
* tryGetByKeyWithFallback(const UResourceBundle
* rb
, const char* path
, UResourceBundle
** fillIn
, FallbackFlags flags
, UErrorCode
& status
) {
601 if (U_FAILURE(status
)) {
604 UBool must
= (flags
& MUST
);
607 status
= U_MISSING_RESOURCE_ERROR
;
611 UResourceBundle
* result
= NULL
;
612 UResourceBundle
* ownedByUs
= NULL
;
613 if (fillIn
== NULL
) {
614 ownedByUs
= ures_getByKeyWithFallback(rb
, path
, NULL
, &status
);
617 *fillIn
= ures_getByKeyWithFallback(rb
, path
, *fillIn
, &status
);
620 if (U_FAILURE(status
)) {
621 ures_close(ownedByUs
);
622 if (status
== U_MISSING_RESOURCE_ERROR
&& !must
) {
623 status
= U_ZERO_ERROR
;
627 flags
= (FallbackFlags
) (flags
& ~MUST
);
631 UBool bRoot
= isRoot(result
, status
);
632 if (bRoot
|| U_FAILURE(status
)) {
633 ures_close(ownedByUs
);
634 if (must
&& (status
== U_ZERO_ERROR
)) {
635 status
= U_MISSING_RESOURCE_ERROR
;
644 ures_close(ownedByUs
);
645 status
= U_ILLEGAL_ARGUMENT_ERROR
;
650 static UBool
isRoot(const UResourceBundle
* rb
, UErrorCode
& status
) {
651 const char* actualLocale
= ures_getLocaleByType(
652 rb
, ULOC_ACTUAL_LOCALE
, &status
);
653 if (U_FAILURE(status
)) {
656 return uprv_strcmp(actualLocale
, gRoot
) == 0;
660 // initCDFLocaleStyleData loads formatting data for a particular style.
661 // decimalFormatBundle is the "decimalFormat" resource bundle in CLDR.
662 // Loaded data stored in result.
663 static void initCDFLocaleStyleData(const UResourceBundle
* decimalFormatBundle
, CDFLocaleStyleData
* result
, UErrorCode
& status
) {
664 if (U_FAILURE(status
)) {
667 // Iterate through all the powers of 10.
668 int32_t size
= ures_getSize(decimalFormatBundle
);
669 UResourceBundle
* power10
= NULL
;
670 for (int32_t i
= 0; i
< size
; ++i
) {
671 power10
= ures_getByIndex(decimalFormatBundle
, i
, power10
, &status
);
672 if (U_FAILURE(status
)) {
676 populatePower10(power10
, result
, status
);
677 if (U_FAILURE(status
)) {
683 fillInMissing(result
);
686 // populatePower10 grabs data for a particular power of 10 from CLDR.
687 // The loaded data is stored in result.
688 static void populatePower10(const UResourceBundle
* power10Bundle
, CDFLocaleStyleData
* result
, UErrorCode
& status
) {
689 if (U_FAILURE(status
)) {
693 double power10
= uprv_strtod(ures_getKey(power10Bundle
), &endPtr
);
695 status
= U_INTERNAL_PROGRAM_ERROR
;
698 int32_t log10Value
= computeLog10(power10
, FALSE
);
699 // Silently ignore divisors that are too big.
700 if (log10Value
== MAX_DIGITS
) {
703 int32_t size
= ures_getSize(power10Bundle
);
704 int32_t numZeros
= 0;
705 UBool otherVariantDefined
= FALSE
;
706 UResourceBundle
* variantBundle
= NULL
;
707 // Iterate over all the plural variants for the power of 10
708 for (int32_t i
= 0; i
< size
; ++i
) {
709 variantBundle
= ures_getByIndex(power10Bundle
, i
, variantBundle
, &status
);
710 if (U_FAILURE(status
)) {
711 ures_close(variantBundle
);
714 const char* variant
= ures_getKey(variantBundle
);
716 const UChar
* formatStrP
= ures_getString(variantBundle
, &resLen
, &status
);
717 if (U_FAILURE(status
)) {
718 ures_close(variantBundle
);
721 UnicodeString
formatStr(false, formatStrP
, resLen
);
722 if (uprv_strcmp(variant
, gOther
) == 0) {
723 otherVariantDefined
= TRUE
;
725 int32_t nz
= populatePrefixSuffix(
726 variant
, log10Value
, formatStr
, result
->unitsByVariant
, status
);
727 if (U_FAILURE(status
)) {
728 ures_close(variantBundle
);
731 if (nz
!= numZeros
) {
732 // We expect all format strings to have the same number of 0's
733 // left of the decimal point.
735 status
= U_INTERNAL_PROGRAM_ERROR
;
736 ures_close(variantBundle
);
742 ures_close(variantBundle
);
743 // We expect to find an OTHER variant for each power of 10.
744 if (!otherVariantDefined
) {
745 status
= U_INTERNAL_PROGRAM_ERROR
;
748 double divisor
= power10
;
749 for (int32_t i
= 1; i
< numZeros
; ++i
) {
752 result
->divisors
[log10Value
] = divisor
;
755 // populatePrefixSuffix Adds a specific prefix-suffix pair to result for a
756 // given variant and log10 value.
757 // variant is 'zero', 'one', 'two', 'few', 'many', or 'other'.
758 // formatStr is the format string from which the prefix and suffix are
759 // extracted. It is usually of form 'Pefix 000 suffix'.
760 // populatePrefixSuffix returns the number of 0's found in formatStr
761 // before the decimal point.
762 // In the special case that formatStr contains only spaces for prefix
763 // and suffix, populatePrefixSuffix returns log10Value + 1.
764 static int32_t populatePrefixSuffix(
765 const char* variant
, int32_t log10Value
, const UnicodeString
& formatStr
, UHashtable
* result
, UErrorCode
& status
) {
766 if (U_FAILURE(status
)) {
769 int32_t firstIdx
= formatStr
.indexOf(kZero
, UPRV_LENGTHOF(kZero
), 0);
770 // We must have 0's in format string.
771 if (firstIdx
== -1) {
772 status
= U_INTERNAL_PROGRAM_ERROR
;
775 int32_t lastIdx
= formatStr
.lastIndexOf(kZero
, UPRV_LENGTHOF(kZero
), firstIdx
);
776 CDFUnit
* unit
= createCDFUnit(variant
, log10Value
, result
, status
);
777 if (U_FAILURE(status
)) {
780 // Everything up to first 0 is the prefix
781 unit
->prefix
= formatStr
.tempSubString(0, firstIdx
);
782 fixQuotes(unit
->prefix
);
783 // Everything beyond the last 0 is the suffix
784 unit
->suffix
= formatStr
.tempSubString(lastIdx
+ 1);
785 fixQuotes(unit
->suffix
);
787 // If there is effectively no prefix or suffix, ignore the actual number of
788 // 0's and act as if the number of 0's matches the size of the number.
789 if (onlySpaces(unit
->prefix
) && onlySpaces(unit
->suffix
)) {
790 return log10Value
+ 1;
793 // Calculate number of zeros before decimal point
794 int32_t idx
= firstIdx
+ 1;
795 while (idx
<= lastIdx
&& formatStr
.charAt(idx
) == u_0
) {
798 return (idx
- firstIdx
);
801 static UBool
onlySpaces(UnicodeString u
) {
802 return u
.trim().length() == 0;
805 // fixQuotes unescapes single quotes. Don''t -> Don't. Letter 'j' -> Letter j.
806 // Modifies s in place.
807 static void fixQuotes(UnicodeString
& s
) {
808 QuoteState state
= OUTSIDE
;
809 int32_t len
= s
.length();
811 for (int32_t i
= 0; i
< len
; ++i
) {
812 UChar ch
= s
.charAt(i
);
814 if (state
== INSIDE_EMPTY
) {
815 s
.setCharAt(dest
, ch
);
819 s
.setCharAt(dest
, ch
);
826 state
= ch
== u_apos
? INSIDE_EMPTY
: OUTSIDE
;
830 state
= ch
== u_apos
? OUTSIDE
: INSIDE_FULL
;
839 // fillInMissing ensures that the data in result is complete.
840 // result data is complete if for each variant in result, there exists
841 // a prefix-suffix pair for each log10 value and there also exists
842 // a divisor for each log10 value.
844 // First this function figures out for which log10 values, the other
845 // variant already had data. These are the same log10 values defined
848 // For each log10 value not defined in CLDR, it uses the divisor for
849 // the last defined log10 value or 1.
851 // Then for each variant, it does the following. For each log10
852 // value not defined in CLDR, copy the prefix-suffix pair from the
853 // previous log10 value. If log10 value is defined in CLDR but is
854 // missing from given variant, copy the prefix-suffix pair for that
855 // log10 value from the 'other' variant.
856 static void fillInMissing(CDFLocaleStyleData
* result
) {
857 const CDFUnit
* otherUnits
=
858 (const CDFUnit
*) uhash_get(result
->unitsByVariant
, gOther
);
859 UBool definedInCLDR
[MAX_DIGITS
];
860 double lastDivisor
= 1.0;
861 for (int32_t i
= 0; i
< MAX_DIGITS
; ++i
) {
862 if (!otherUnits
[i
].isSet()) {
863 result
->divisors
[i
] = lastDivisor
;
864 definedInCLDR
[i
] = FALSE
;
866 lastDivisor
= result
->divisors
[i
];
867 definedInCLDR
[i
] = TRUE
;
870 // Iterate over each variant.
871 int32_t pos
= UHASH_FIRST
;
872 const UHashElement
* element
= uhash_nextElement(result
->unitsByVariant
, &pos
);
873 for (;element
!= NULL
; element
= uhash_nextElement(result
->unitsByVariant
, &pos
)) {
874 CDFUnit
* units
= (CDFUnit
*) element
->value
.pointer
;
875 for (int32_t i
= 0; i
< MAX_DIGITS
; ++i
) {
876 if (definedInCLDR
[i
]) {
877 if (!units
[i
].isSet()) {
878 units
[i
] = otherUnits
[i
];
882 units
[0].markAsSet();
884 units
[i
] = units
[i
- 1];
891 // computeLog10 computes floor(log10(x)). If inRange is TRUE, the biggest
892 // value computeLog10 will return MAX_DIGITS -1 even for
893 // numbers > 10^MAX_DIGITS. If inRange is FALSE, computeLog10 will return
895 static int32_t computeLog10(double x
, UBool inRange
) {
897 int32_t max
= inRange
? MAX_DIGITS
- 1 : MAX_DIGITS
;
908 // createCDFUnit returns a pointer to the prefix-suffix pair for a given
909 // variant and log10 value within table. If no such prefix-suffix pair is
910 // stored in table, one is created within table before returning pointer.
911 static CDFUnit
* createCDFUnit(const char* variant
, int32_t log10Value
, UHashtable
* table
, UErrorCode
& status
) {
912 if (U_FAILURE(status
)) {
915 CDFUnit
*cdfUnit
= (CDFUnit
*) uhash_get(table
, variant
);
916 if (cdfUnit
== NULL
) {
917 cdfUnit
= new CDFUnit
[MAX_DIGITS
];
918 if (cdfUnit
== NULL
) {
919 status
= U_MEMORY_ALLOCATION_ERROR
;
922 uhash_put(table
, uprv_strdup(variant
), cdfUnit
, &status
);
923 if (U_FAILURE(status
)) {
927 CDFUnit
* result
= &cdfUnit
[log10Value
];
932 // getCDFUnitFallback returns a pointer to the prefix-suffix pair for a given
933 // variant and log10 value within table. If the given variant doesn't exist, it
934 // falls back to the OTHER variant. Therefore, this method will always return
935 // some non-NULL value.
936 static const CDFUnit
* getCDFUnitFallback(const UHashtable
* table
, const UnicodeString
& variant
, int32_t log10Value
) {
938 UErrorCode status
= U_ZERO_ERROR
;
939 const CDFUnit
*cdfUnit
= NULL
;
940 cvariant
.appendInvariantChars(variant
, status
);
941 if (!U_FAILURE(status
)) {
942 cdfUnit
= (const CDFUnit
*) uhash_get(table
, cvariant
.data());
944 if (cdfUnit
== NULL
) {
945 cdfUnit
= (const CDFUnit
*) uhash_get(table
, gOther
);
947 return &cdfUnit
[log10Value
];