2 *******************************************************************************
3 * Copyright (C) 1997-2015, 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 UErrorCode status
= U_ZERO_ERROR
;
246 return format(number
, appendTo
, pos
, status
);
250 CompactDecimalFormat::format(
252 UnicodeString
& appendTo
,
254 UErrorCode
&status
) const {
255 if (U_FAILURE(status
)) {
258 DigitList orig
, rounded
;
261 _round(orig
, rounded
, isNegative
, status
);
262 if (U_FAILURE(status
)) {
265 double roundedDouble
= rounded
.getDouble();
267 roundedDouble
= -roundedDouble
;
269 int32_t baseIdx
= computeLog10(roundedDouble
, TRUE
);
270 double numberToFormat
= roundedDouble
/ _divisors
[baseIdx
];
271 UnicodeString variant
= _pluralRules
->select(numberToFormat
);
273 numberToFormat
= -numberToFormat
;
275 const CDFUnit
* unit
= getCDFUnitFallback(_unitsByVariant
, variant
, baseIdx
);
276 appendTo
+= unit
->prefix
;
277 DecimalFormat::format(numberToFormat
, appendTo
, pos
);
278 appendTo
+= unit
->suffix
;
283 CompactDecimalFormat::format(
285 UnicodeString
& appendTo
,
286 FieldPositionIterator
* /* posIter */,
287 UErrorCode
& status
) const {
288 status
= U_UNSUPPORTED_ERROR
;
293 CompactDecimalFormat::format(
295 UnicodeString
& appendTo
,
296 FieldPosition
& pos
) const {
297 return format((double) number
, appendTo
, pos
);
301 CompactDecimalFormat::format(
303 UnicodeString
& appendTo
,
305 UErrorCode
&status
) const {
306 return format((double) number
, appendTo
, pos
, status
);
310 CompactDecimalFormat::format(
311 int32_t /* number */,
312 UnicodeString
& appendTo
,
313 FieldPositionIterator
* /* posIter */,
314 UErrorCode
& status
) const {
315 status
= U_UNSUPPORTED_ERROR
;
320 CompactDecimalFormat::format(
322 UnicodeString
& appendTo
,
323 FieldPosition
& pos
) const {
324 return format((double) number
, appendTo
, pos
);
328 CompactDecimalFormat::format(
330 UnicodeString
& appendTo
,
332 UErrorCode
&status
) const {
333 return format((double) number
, appendTo
, pos
, status
);
337 CompactDecimalFormat::format(
338 int64_t /* number */,
339 UnicodeString
& appendTo
,
340 FieldPositionIterator
* /* posIter */,
341 UErrorCode
& status
) const {
342 status
= U_UNSUPPORTED_ERROR
;
347 CompactDecimalFormat::format(
348 const StringPiece
& /* number */,
349 UnicodeString
& appendTo
,
350 FieldPositionIterator
* /* posIter */,
351 UErrorCode
& status
) const {
352 status
= U_UNSUPPORTED_ERROR
;
357 CompactDecimalFormat::format(
358 const DigitList
& /* number */,
359 UnicodeString
& appendTo
,
360 FieldPositionIterator
* /* posIter */,
361 UErrorCode
& status
) const {
362 status
= U_UNSUPPORTED_ERROR
;
367 CompactDecimalFormat::format(const DigitList
& /* number */,
368 UnicodeString
& appendTo
,
369 FieldPosition
& /* pos */,
370 UErrorCode
& status
) const {
371 status
= U_UNSUPPORTED_ERROR
;
376 CompactDecimalFormat::parse(
377 const UnicodeString
& /* text */,
378 Formattable
& /* result */,
379 ParsePosition
& /* parsePosition */) const {
383 CompactDecimalFormat::parse(
384 const UnicodeString
& /* text */,
385 Formattable
& /* result */,
386 UErrorCode
& status
) const {
387 status
= U_UNSUPPORTED_ERROR
;
391 CompactDecimalFormat::parseCurrency(
392 const UnicodeString
& /* text */,
393 ParsePosition
& /* pos */) const {
397 void CDFLocaleStyleData::Init(UErrorCode
& status
) {
398 if (unitsByVariant
!= NULL
) {
401 unitsByVariant
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
402 if (U_FAILURE(status
)) {
405 uhash_setKeyDeleter(unitsByVariant
, uprv_free
);
406 uhash_setValueDeleter(unitsByVariant
, deleteCDFUnits
);
409 CDFLocaleStyleData::~CDFLocaleStyleData() {
413 void CDFLocaleStyleData::setToBogus() {
414 if (unitsByVariant
!= NULL
) {
415 uhash_close(unitsByVariant
);
416 unitsByVariant
= NULL
;
420 void CDFLocaleData::Init(UErrorCode
& status
) {
421 shortData
.Init(status
);
422 if (U_FAILURE(status
)) {
425 longData
.Init(status
);
428 // Helper method for operator=
429 static UBool
divisors_equal(const double* lhs
, const double* rhs
) {
430 for (int32_t i
= 0; i
< MAX_DIGITS
; ++i
) {
431 if (lhs
[i
] != rhs
[i
]) {
438 // getCDFLocaleStyleData returns pointer to formatting data for given locale and
439 // style within the global cache. On cache miss, getCDFLocaleStyleData loads
440 // the data from CLDR into the global cache before returning the pointer. If a
441 // UNUM_LONG data is requested for a locale, and that locale does not have
442 // UNUM_LONG data, getCDFLocaleStyleData will fall back to UNUM_SHORT data for
444 static const CDFLocaleStyleData
* getCDFLocaleStyleData(const Locale
& inLocale
, UNumberCompactStyle style
, UErrorCode
& status
) {
445 if (U_FAILURE(status
)) {
448 CDFLocaleData
* result
= NULL
;
449 const char* key
= inLocale
.getName();
451 Mutex
lock(&gCompactDecimalMetaLock
);
452 if (gCompactDecimalData
== NULL
) {
453 gCompactDecimalData
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
454 if (U_FAILURE(status
)) {
457 uhash_setKeyDeleter(gCompactDecimalData
, uprv_free
);
458 uhash_setValueDeleter(gCompactDecimalData
, deleteCDFLocaleData
);
459 ucln_i18n_registerCleanup(UCLN_I18N_CDFINFO
, cdf_cleanup
);
461 result
= (CDFLocaleData
*) uhash_get(gCompactDecimalData
, key
);
464 if (result
!= NULL
) {
465 return extractDataByStyleEnum(*result
, style
, status
);
468 result
= loadCDFLocaleData(inLocale
, status
);
469 if (U_FAILURE(status
)) {
474 Mutex
lock(&gCompactDecimalMetaLock
);
475 CDFLocaleData
* temp
= (CDFLocaleData
*) uhash_get(gCompactDecimalData
, key
);
480 uhash_put(gCompactDecimalData
, uprv_strdup(key
), (void*) result
, &status
);
481 if (U_FAILURE(status
)) {
486 return extractDataByStyleEnum(*result
, style
, status
);
489 static const CDFLocaleStyleData
* extractDataByStyleEnum(const CDFLocaleData
& data
, UNumberCompactStyle style
, UErrorCode
& status
) {
492 return &data
.shortData
;
494 if (!data
.longData
.isBogus()) {
495 return &data
.longData
;
497 return &data
.shortData
;
499 status
= U_ILLEGAL_ARGUMENT_ERROR
;
504 // loadCDFLocaleData loads formatting data from CLDR for a given locale. The
505 // caller owns the returned pointer.
506 static CDFLocaleData
* loadCDFLocaleData(const Locale
& inLocale
, UErrorCode
& status
) {
507 if (U_FAILURE(status
)) {
510 CDFLocaleData
* result
= new CDFLocaleData
;
511 if (result
== NULL
) {
512 status
= U_MEMORY_ALLOCATION_ERROR
;
515 result
->Init(status
);
516 if (U_FAILURE(status
)) {
521 initCDFLocaleData(inLocale
, result
, status
);
522 if (U_FAILURE(status
)) {
529 // initCDFLocaleData initializes result with data from CLDR.
530 // inLocale is the locale, the CLDR data is stored in result.
531 // We load the UNUM_SHORT and UNUM_LONG data looking first in local numbering
532 // system and not including root locale in fallback. Next we try in the latn
533 // numbering system where we fallback all the way to root. If we don't find
534 // UNUM_SHORT data in these three places, we report an error. If we find
535 // UNUM_SHORT data before finding UNUM_LONG data we make UNUM_LONG data fall
536 // back to UNUM_SHORT data.
537 static void initCDFLocaleData(const Locale
& inLocale
, CDFLocaleData
* result
, UErrorCode
& status
) {
538 LocalPointer
<NumberingSystem
> ns(NumberingSystem::createInstance(inLocale
, status
));
539 if (U_FAILURE(status
)) {
542 const char* numberingSystemName
= ns
->getName();
543 UResourceBundle
* rb
= ures_open(NULL
, inLocale
.getName(), &status
);
544 rb
= ures_getByKeyWithFallback(rb
, gNumberElementsTag
, rb
, &status
);
545 if (U_FAILURE(status
)) {
549 UResourceBundle
* shortDataFillIn
= NULL
;
550 UResourceBundle
* longDataFillIn
= NULL
;
551 UResourceBundle
* shortData
= NULL
;
552 UResourceBundle
* longData
= NULL
;
554 if (uprv_strcmp(numberingSystemName
, gLatnTag
) != 0) {
555 LocalUResourceBundlePointer
localResource(
556 tryGetByKeyWithFallback(rb
, numberingSystemName
, NULL
, NOT_ROOT
, status
));
557 shortData
= tryGetDecimalFallback(
558 localResource
.getAlias(), gPatternsShort
, &shortDataFillIn
, NOT_ROOT
, status
);
559 longData
= tryGetDecimalFallback(
560 localResource
.getAlias(), gPatternsLong
, &longDataFillIn
, NOT_ROOT
, status
);
562 if (U_FAILURE(status
)) {
563 ures_close(shortDataFillIn
);
564 ures_close(longDataFillIn
);
569 // If we haven't found UNUM_SHORT look in latn numbering system. We must
570 // succeed at finding UNUM_SHORT here.
571 if (shortData
== NULL
) {
572 LocalUResourceBundlePointer
latnResource(tryGetByKeyWithFallback(rb
, gLatnTag
, NULL
, MUST
, status
));
573 shortData
= tryGetDecimalFallback(latnResource
.getAlias(), gPatternsShort
, &shortDataFillIn
, MUST
, status
);
574 if (longData
== NULL
) {
575 longData
= tryGetDecimalFallback(latnResource
.getAlias(), gPatternsLong
, &longDataFillIn
, ANY
, status
);
576 if (longData
!= NULL
&& isRoot(longData
, status
) && !isRoot(shortData
, status
)) {
581 initCDFLocaleStyleData(shortData
, &result
->shortData
, status
);
582 ures_close(shortDataFillIn
);
583 if (U_FAILURE(status
)) {
584 ures_close(longDataFillIn
);
588 if (longData
== NULL
) {
589 result
->longData
.setToBogus();
591 initCDFLocaleStyleData(longData
, &result
->longData
, status
);
593 ures_close(longDataFillIn
);
598 * tryGetDecimalFallback attempts to fetch the "decimalFormat" resource bundle
599 * with a particular style. style is either "patternsShort" or "patternsLong."
600 * FillIn, flags, and status work in the same way as in tryGetByKeyWithFallback.
602 static UResourceBundle
* tryGetDecimalFallback(const UResourceBundle
* numberSystemResource
, const char* style
, UResourceBundle
** fillIn
, FallbackFlags flags
, UErrorCode
& status
) {
603 UResourceBundle
* first
= tryGetByKeyWithFallback(numberSystemResource
, style
, fillIn
, flags
, status
);
604 UResourceBundle
* second
= tryGetByKeyWithFallback(first
, gDecimalFormatTag
, fillIn
, flags
, status
);
605 if (fillIn
== NULL
) {
611 // tryGetByKeyWithFallback returns a sub-resource bundle that matches given
612 // criteria or NULL if none found. rb is the resource bundle that we are
613 // searching. If rb == NULL then this function behaves as if no sub-resource
614 // is found; path is the key of the sub-resource,
615 // (i.e "foo" but not "foo/bar"); If fillIn is NULL, caller must always call
616 // ures_close() on returned resource. See below for example when fillIn is
617 // not NULL. flags is ANY or NOT_ROOT. Optionally, these values
618 // can be ored with MUST. MUST by itself is the same as ANY | MUST.
619 // The locale of the returned sub-resource will either match the
620 // flags or the returned sub-resouce will be NULL. If MUST is included in
621 // flags, and not suitable sub-resource is found then in addition to returning
622 // NULL, this function also sets status to U_MISSING_RESOURCE_ERROR. If MUST
623 // is not included in flags, then this function just returns NULL if no
624 // such sub-resource is found and will never set status to
625 // U_MISSING_RESOURCE_ERROR.
627 // Example: This code first searches for "foo/bar" sub-resource without falling
628 // back to ROOT. Then searches for "baz" sub-resource as last resort.
630 // UResourcebundle* fillIn = NULL;
631 // UResourceBundle* data = tryGetByKeyWithFallback(rb, "foo", &fillIn, NON_ROOT, status);
632 // data = tryGetByKeyWithFallback(data, "bar", &fillIn, NON_ROOT, status);
634 // data = tryGetbyKeyWithFallback(rb, "baz", &fillIn, MUST, status);
636 // if (U_FAILURE(status)) {
637 // ures_close(fillIn);
640 // doStuffWithNonNullSubresource(data);
642 // /* Wrong! don't do the following as it can leak memory if fillIn gets set
644 // fillIn = tryGetByKeyWithFallback(rb, "wrong", &fillIn, ANY, status);
646 // ures_close(fillIn);
648 static UResourceBundle
* tryGetByKeyWithFallback(const UResourceBundle
* rb
, const char* path
, UResourceBundle
** fillIn
, FallbackFlags flags
, UErrorCode
& status
) {
649 if (U_FAILURE(status
)) {
652 UBool must
= (flags
& MUST
);
655 status
= U_MISSING_RESOURCE_ERROR
;
659 UResourceBundle
* result
= NULL
;
660 UResourceBundle
* ownedByUs
= NULL
;
661 if (fillIn
== NULL
) {
662 ownedByUs
= ures_getByKeyWithFallback(rb
, path
, NULL
, &status
);
665 *fillIn
= ures_getByKeyWithFallback(rb
, path
, *fillIn
, &status
);
668 if (U_FAILURE(status
)) {
669 ures_close(ownedByUs
);
670 if (status
== U_MISSING_RESOURCE_ERROR
&& !must
) {
671 status
= U_ZERO_ERROR
;
675 flags
= (FallbackFlags
) (flags
& ~MUST
);
679 UBool bRoot
= isRoot(result
, status
);
680 if (bRoot
|| U_FAILURE(status
)) {
681 ures_close(ownedByUs
);
682 if (must
&& (status
== U_ZERO_ERROR
)) {
683 status
= U_MISSING_RESOURCE_ERROR
;
692 ures_close(ownedByUs
);
693 status
= U_ILLEGAL_ARGUMENT_ERROR
;
698 static UBool
isRoot(const UResourceBundle
* rb
, UErrorCode
& status
) {
699 const char* actualLocale
= ures_getLocaleByType(
700 rb
, ULOC_ACTUAL_LOCALE
, &status
);
701 if (U_FAILURE(status
)) {
704 return uprv_strcmp(actualLocale
, gRoot
) == 0;
708 // initCDFLocaleStyleData loads formatting data for a particular style.
709 // decimalFormatBundle is the "decimalFormat" resource bundle in CLDR.
710 // Loaded data stored in result.
711 static void initCDFLocaleStyleData(const UResourceBundle
* decimalFormatBundle
, CDFLocaleStyleData
* result
, UErrorCode
& status
) {
712 if (U_FAILURE(status
)) {
715 // Iterate through all the powers of 10.
716 int32_t size
= ures_getSize(decimalFormatBundle
);
717 UResourceBundle
* power10
= NULL
;
718 for (int32_t i
= 0; i
< size
; ++i
) {
719 power10
= ures_getByIndex(decimalFormatBundle
, i
, power10
, &status
);
720 if (U_FAILURE(status
)) {
724 populatePower10(power10
, result
, status
);
725 if (U_FAILURE(status
)) {
731 fillInMissing(result
);
734 // populatePower10 grabs data for a particular power of 10 from CLDR.
735 // The loaded data is stored in result.
736 static void populatePower10(const UResourceBundle
* power10Bundle
, CDFLocaleStyleData
* result
, UErrorCode
& status
) {
737 if (U_FAILURE(status
)) {
741 double power10
= uprv_strtod(ures_getKey(power10Bundle
), &endPtr
);
743 status
= U_INTERNAL_PROGRAM_ERROR
;
746 int32_t log10Value
= computeLog10(power10
, FALSE
);
747 // Silently ignore divisors that are too big.
748 if (log10Value
== MAX_DIGITS
) {
751 int32_t size
= ures_getSize(power10Bundle
);
752 int32_t numZeros
= 0;
753 UBool otherVariantDefined
= FALSE
;
754 UResourceBundle
* variantBundle
= NULL
;
755 // Iterate over all the plural variants for the power of 10
756 for (int32_t i
= 0; i
< size
; ++i
) {
757 variantBundle
= ures_getByIndex(power10Bundle
, i
, variantBundle
, &status
);
758 if (U_FAILURE(status
)) {
759 ures_close(variantBundle
);
762 const char* variant
= ures_getKey(variantBundle
);
764 const UChar
* formatStrP
= ures_getString(variantBundle
, &resLen
, &status
);
765 if (U_FAILURE(status
)) {
766 ures_close(variantBundle
);
769 UnicodeString
formatStr(false, formatStrP
, resLen
);
770 if (uprv_strcmp(variant
, gOther
) == 0) {
771 otherVariantDefined
= TRUE
;
773 int32_t nz
= populatePrefixSuffix(
774 variant
, log10Value
, formatStr
, result
->unitsByVariant
, status
);
775 if (U_FAILURE(status
)) {
776 ures_close(variantBundle
);
779 if (nz
!= numZeros
) {
780 // We expect all format strings to have the same number of 0's
781 // left of the decimal point.
783 status
= U_INTERNAL_PROGRAM_ERROR
;
784 ures_close(variantBundle
);
790 ures_close(variantBundle
);
791 // We expect to find an OTHER variant for each power of 10.
792 if (!otherVariantDefined
) {
793 status
= U_INTERNAL_PROGRAM_ERROR
;
796 double divisor
= power10
;
797 for (int32_t i
= 1; i
< numZeros
; ++i
) {
800 result
->divisors
[log10Value
] = divisor
;
803 // populatePrefixSuffix Adds a specific prefix-suffix pair to result for a
804 // given variant and log10 value.
805 // variant is 'zero', 'one', 'two', 'few', 'many', or 'other'.
806 // formatStr is the format string from which the prefix and suffix are
807 // extracted. It is usually of form 'Pefix 000 suffix'.
808 // populatePrefixSuffix returns the number of 0's found in formatStr
809 // before the decimal point.
810 // In the special case that formatStr contains only spaces for prefix
811 // and suffix, populatePrefixSuffix returns log10Value + 1.
812 static int32_t populatePrefixSuffix(
813 const char* variant
, int32_t log10Value
, const UnicodeString
& formatStr
, UHashtable
* result
, UErrorCode
& status
) {
814 if (U_FAILURE(status
)) {
817 int32_t firstIdx
= formatStr
.indexOf(kZero
, UPRV_LENGTHOF(kZero
), 0);
818 // We must have 0's in format string.
819 if (firstIdx
== -1) {
820 status
= U_INTERNAL_PROGRAM_ERROR
;
823 int32_t lastIdx
= formatStr
.lastIndexOf(kZero
, UPRV_LENGTHOF(kZero
), firstIdx
);
824 CDFUnit
* unit
= createCDFUnit(variant
, log10Value
, result
, status
);
825 if (U_FAILURE(status
)) {
828 // Everything up to first 0 is the prefix
829 unit
->prefix
= formatStr
.tempSubString(0, firstIdx
);
830 fixQuotes(unit
->prefix
);
831 // Everything beyond the last 0 is the suffix
832 unit
->suffix
= formatStr
.tempSubString(lastIdx
+ 1);
833 fixQuotes(unit
->suffix
);
835 // If there is effectively no prefix or suffix, ignore the actual number of
836 // 0's and act as if the number of 0's matches the size of the number.
837 if (onlySpaces(unit
->prefix
) && onlySpaces(unit
->suffix
)) {
838 return log10Value
+ 1;
841 // Calculate number of zeros before decimal point
842 int32_t idx
= firstIdx
+ 1;
843 while (idx
<= lastIdx
&& formatStr
.charAt(idx
) == u_0
) {
846 return (idx
- firstIdx
);
849 static UBool
onlySpaces(UnicodeString u
) {
850 return u
.trim().length() == 0;
853 // fixQuotes unescapes single quotes. Don''t -> Don't. Letter 'j' -> Letter j.
854 // Modifies s in place.
855 static void fixQuotes(UnicodeString
& s
) {
856 QuoteState state
= OUTSIDE
;
857 int32_t len
= s
.length();
859 for (int32_t i
= 0; i
< len
; ++i
) {
860 UChar ch
= s
.charAt(i
);
862 if (state
== INSIDE_EMPTY
) {
863 s
.setCharAt(dest
, ch
);
867 s
.setCharAt(dest
, ch
);
874 state
= ch
== u_apos
? INSIDE_EMPTY
: OUTSIDE
;
878 state
= ch
== u_apos
? OUTSIDE
: INSIDE_FULL
;
887 // fillInMissing ensures that the data in result is complete.
888 // result data is complete if for each variant in result, there exists
889 // a prefix-suffix pair for each log10 value and there also exists
890 // a divisor for each log10 value.
892 // First this function figures out for which log10 values, the other
893 // variant already had data. These are the same log10 values defined
896 // For each log10 value not defined in CLDR, it uses the divisor for
897 // the last defined log10 value or 1.
899 // Then for each variant, it does the following. For each log10
900 // value not defined in CLDR, copy the prefix-suffix pair from the
901 // previous log10 value. If log10 value is defined in CLDR but is
902 // missing from given variant, copy the prefix-suffix pair for that
903 // log10 value from the 'other' variant.
904 static void fillInMissing(CDFLocaleStyleData
* result
) {
905 const CDFUnit
* otherUnits
=
906 (const CDFUnit
*) uhash_get(result
->unitsByVariant
, gOther
);
907 UBool definedInCLDR
[MAX_DIGITS
];
908 double lastDivisor
= 1.0;
909 for (int32_t i
= 0; i
< MAX_DIGITS
; ++i
) {
910 if (!otherUnits
[i
].isSet()) {
911 result
->divisors
[i
] = lastDivisor
;
912 definedInCLDR
[i
] = FALSE
;
914 lastDivisor
= result
->divisors
[i
];
915 definedInCLDR
[i
] = TRUE
;
918 // Iterate over each variant.
919 int32_t pos
= UHASH_FIRST
;
920 const UHashElement
* element
= uhash_nextElement(result
->unitsByVariant
, &pos
);
921 for (;element
!= NULL
; element
= uhash_nextElement(result
->unitsByVariant
, &pos
)) {
922 CDFUnit
* units
= (CDFUnit
*) element
->value
.pointer
;
923 for (int32_t i
= 0; i
< MAX_DIGITS
; ++i
) {
924 if (definedInCLDR
[i
]) {
925 if (!units
[i
].isSet()) {
926 units
[i
] = otherUnits
[i
];
930 units
[0].markAsSet();
932 units
[i
] = units
[i
- 1];
939 // computeLog10 computes floor(log10(x)). If inRange is TRUE, the biggest
940 // value computeLog10 will return MAX_DIGITS -1 even for
941 // numbers > 10^MAX_DIGITS. If inRange is FALSE, computeLog10 will return
943 static int32_t computeLog10(double x
, UBool inRange
) {
945 int32_t max
= inRange
? MAX_DIGITS
- 1 : MAX_DIGITS
;
956 // createCDFUnit returns a pointer to the prefix-suffix pair for a given
957 // variant and log10 value within table. If no such prefix-suffix pair is
958 // stored in table, one is created within table before returning pointer.
959 static CDFUnit
* createCDFUnit(const char* variant
, int32_t log10Value
, UHashtable
* table
, UErrorCode
& status
) {
960 if (U_FAILURE(status
)) {
963 CDFUnit
*cdfUnit
= (CDFUnit
*) uhash_get(table
, variant
);
964 if (cdfUnit
== NULL
) {
965 cdfUnit
= new CDFUnit
[MAX_DIGITS
];
966 if (cdfUnit
== NULL
) {
967 status
= U_MEMORY_ALLOCATION_ERROR
;
970 uhash_put(table
, uprv_strdup(variant
), cdfUnit
, &status
);
971 if (U_FAILURE(status
)) {
975 CDFUnit
* result
= &cdfUnit
[log10Value
];
980 // getCDFUnitFallback returns a pointer to the prefix-suffix pair for a given
981 // variant and log10 value within table. If the given variant doesn't exist, it
982 // falls back to the OTHER variant. Therefore, this method will always return
983 // some non-NULL value.
984 static const CDFUnit
* getCDFUnitFallback(const UHashtable
* table
, const UnicodeString
& variant
, int32_t log10Value
) {
986 UErrorCode status
= U_ZERO_ERROR
;
987 const CDFUnit
*cdfUnit
= NULL
;
988 cvariant
.appendInvariantChars(variant
, status
);
989 if (!U_FAILURE(status
)) {
990 cdfUnit
= (const CDFUnit
*) uhash_get(table
, cvariant
.data());
992 if (cdfUnit
== NULL
) {
993 cdfUnit
= (const CDFUnit
*) uhash_get(table
, gOther
);
995 return &cdfUnit
[log10Value
];