2 *******************************************************************************
3 * Copyright (C) 1997-2012, 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 #define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
31 // Maps locale name to CDFLocaleData struct.
32 static UHashtable
* gCompactDecimalData
= NULL
;
33 static UMutex gCompactDecimalMetaLock
= U_MUTEX_INITIALIZER
;
37 static const int32_t MAX_DIGITS
= 15;
38 static const char gOther
[] = "other";
39 static const char gLatnTag
[] = "latn";
40 static const char gNumberElementsTag
[] = "NumberElements";
41 static const char gDecimalFormatTag
[] = "decimalFormat";
42 static const char gPatternsShort
[] = "patternsShort";
43 static const char gPatternsLong
[] = "patternsLong";
44 static const char gRoot
[] = "root";
46 static const UChar u_0
= 0x30;
47 static const UChar u_apos
= 0x27;
49 static const UChar kZero
[] = {u_0
};
51 // Used to unescape single quotes.
62 // Next one will be 4 then 6 etc.
66 // CDFUnit represents a prefix-suffix pair for a particular variant
68 struct CDFUnit
: public UMemory
{
71 inline CDFUnit() : prefix(), suffix() {
75 inline UBool
isSet() const {
76 return !prefix
.isBogus();
78 inline void markAsSet() {
83 // CDFLocaleStyleData contains formatting data for a particular locale
85 class CDFLocaleStyleData
: public UMemory
{
87 // What to divide by for each log10 value when formatting. These values
88 // will be powers of 10. For English, would be:
89 // 1, 1, 1, 1000, 1000, 1000, 1000000, 1000000, 1000000, 1000000000 ...
90 double divisors
[MAX_DIGITS
];
91 // Maps plural variants to CDFUnit[MAX_DIGITS] arrays.
92 // To format a number x,
93 // first compute log10(x). Compute displayNum = (x / divisors[log10(x)]).
94 // Compute the plural variant for displayNum
95 // (e.g zero, one, two, few, many, other).
96 // Compute cdfUnits = unitsByVariant[pluralVariant].
97 // Prefix and suffix to use at cdfUnits[log10(x)]
98 UHashtable
* unitsByVariant
;
99 inline CDFLocaleStyleData() : unitsByVariant(NULL
) {}
100 ~CDFLocaleStyleData();
101 // Init initializes this object.
102 void Init(UErrorCode
& status
);
103 inline UBool
isBogus() const {
104 return unitsByVariant
== NULL
;
108 CDFLocaleStyleData(const CDFLocaleStyleData
&);
109 CDFLocaleStyleData
& operator=(const CDFLocaleStyleData
&);
112 // CDFLocaleData contains formatting data for a particular locale.
113 struct CDFLocaleData
: public UMemory
{
114 CDFLocaleStyleData shortData
;
115 CDFLocaleStyleData longData
;
116 inline CDFLocaleData() : shortData(), longData() { }
117 inline ~CDFLocaleData() { }
118 // Init initializes this object.
119 void Init(UErrorCode
& status
);
126 static UBool U_CALLCONV
cdf_cleanup(void) {
127 if (gCompactDecimalData
!= NULL
) {
128 uhash_close(gCompactDecimalData
);
129 gCompactDecimalData
= NULL
;
134 static void U_CALLCONV
deleteCDFUnits(void* ptr
) {
135 delete [] (icu::CDFUnit
*) ptr
;
138 static void U_CALLCONV
deleteCDFLocaleData(void* ptr
) {
139 delete (icu::CDFLocaleData
*) ptr
;
146 static UBool
divisors_equal(const double* lhs
, const double* rhs
);
147 static const CDFLocaleStyleData
* getCDFLocaleStyleData(const Locale
& inLocale
, UNumberCompactStyle style
, UErrorCode
& status
);
149 static const CDFLocaleStyleData
* extractDataByStyleEnum(const CDFLocaleData
& data
, UNumberCompactStyle style
, UErrorCode
& status
);
150 static CDFLocaleData
* loadCDFLocaleData(const Locale
& inLocale
, UErrorCode
& status
);
151 static void initCDFLocaleData(const Locale
& inLocale
, CDFLocaleData
* result
, UErrorCode
& status
);
152 static UResourceBundle
* tryGetDecimalFallback(const UResourceBundle
* numberSystemResource
, const char* style
, UResourceBundle
** fillIn
, FallbackFlags flags
, UErrorCode
& status
);
153 static UResourceBundle
* tryGetByKeyWithFallback(const UResourceBundle
* rb
, const char* path
, UResourceBundle
** fillIn
, FallbackFlags flags
, UErrorCode
& status
);
154 static UBool
isRoot(const UResourceBundle
* rb
, UErrorCode
& status
);
155 static void initCDFLocaleStyleData(const UResourceBundle
* decimalFormatBundle
, CDFLocaleStyleData
* result
, UErrorCode
& status
);
156 static void populatePower10(const UResourceBundle
* power10Bundle
, CDFLocaleStyleData
* result
, UErrorCode
& status
);
157 static int32_t populatePrefixSuffix(const char* variant
, int32_t log10Value
, const UnicodeString
& formatStr
, UHashtable
* result
, UErrorCode
& status
);
158 static UBool
onlySpaces(UnicodeString u
);
159 static void fixQuotes(UnicodeString
& s
);
160 static void fillInMissing(CDFLocaleStyleData
* result
);
161 static int32_t computeLog10(double x
, UBool inRange
);
162 static CDFUnit
* createCDFUnit(const char* variant
, int32_t log10Value
, UHashtable
* table
, UErrorCode
& status
);
163 static const CDFUnit
* getCDFUnitFallback(const UHashtable
* table
, const UnicodeString
& variant
, int32_t log10Value
);
165 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompactDecimalFormat
)
167 CompactDecimalFormat::CompactDecimalFormat(
168 const DecimalFormat
& decimalFormat
,
169 const UHashtable
* unitsByVariant
,
170 const double* divisors
,
171 PluralRules
* pluralRules
)
172 : DecimalFormat(decimalFormat
), _unitsByVariant(unitsByVariant
), _divisors(divisors
), _pluralRules(pluralRules
) {
175 CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat
& source
)
176 : DecimalFormat(source
), _unitsByVariant(source
._unitsByVariant
), _divisors(source
._divisors
), _pluralRules(source
._pluralRules
->clone()) {
179 CompactDecimalFormat
* U_EXPORT2
180 CompactDecimalFormat::createInstance(
181 const Locale
& inLocale
, UNumberCompactStyle style
, UErrorCode
& status
) {
182 LocalPointer
<DecimalFormat
> decfmt((DecimalFormat
*) NumberFormat::makeInstance(inLocale
, UNUM_DECIMAL
, TRUE
, status
));
183 if (U_FAILURE(status
)) {
186 LocalPointer
<PluralRules
> pluralRules(PluralRules::forLocale(inLocale
, status
));
187 if (U_FAILURE(status
)) {
190 const CDFLocaleStyleData
* data
= getCDFLocaleStyleData(inLocale
, style
, status
);
191 if (U_FAILURE(status
)) {
194 CompactDecimalFormat
* result
=
195 new CompactDecimalFormat(*decfmt
, data
->unitsByVariant
, data
->divisors
, pluralRules
.getAlias());
196 if (result
== NULL
) {
197 status
= U_MEMORY_ALLOCATION_ERROR
;
200 pluralRules
.orphan();
201 result
->setMaximumSignificantDigits(3);
202 result
->setSignificantDigitsUsed(TRUE
);
203 result
->setGroupingUsed(FALSE
);
207 CompactDecimalFormat
&
208 CompactDecimalFormat::operator=(const CompactDecimalFormat
& rhs
) {
210 DecimalFormat::operator=(rhs
);
211 _unitsByVariant
= rhs
._unitsByVariant
;
212 _divisors
= rhs
._divisors
;
214 _pluralRules
= rhs
._pluralRules
->clone();
219 CompactDecimalFormat::~CompactDecimalFormat() {
225 CompactDecimalFormat::clone(void) const {
226 return new CompactDecimalFormat(*this);
230 CompactDecimalFormat::operator==(const Format
& that
) const {
234 return (DecimalFormat::operator==(that
) && eqHelper((const CompactDecimalFormat
&) that
));
238 CompactDecimalFormat::eqHelper(const CompactDecimalFormat
& that
) const {
239 return uhash_equals(_unitsByVariant
, that
._unitsByVariant
) && divisors_equal(_divisors
, that
._divisors
) && (*_pluralRules
== *that
._pluralRules
);
243 CompactDecimalFormat::format(
245 UnicodeString
& appendTo
,
246 FieldPosition
& pos
) const {
247 DigitList orig
, rounded
;
250 UErrorCode status
= U_ZERO_ERROR
;
251 _round(orig
, rounded
, isNegative
, status
);
252 if (U_FAILURE(status
)) {
255 double roundedDouble
= rounded
.getDouble();
257 roundedDouble
= -roundedDouble
;
259 int32_t baseIdx
= computeLog10(roundedDouble
, TRUE
);
260 double numberToFormat
= roundedDouble
/ _divisors
[baseIdx
];
261 UnicodeString variant
= _pluralRules
->select(numberToFormat
);
263 numberToFormat
= -numberToFormat
;
265 const CDFUnit
* unit
= getCDFUnitFallback(_unitsByVariant
, variant
, baseIdx
);
266 appendTo
+= unit
->prefix
;
267 DecimalFormat::format(numberToFormat
, appendTo
, pos
);
268 appendTo
+= unit
->suffix
;
273 CompactDecimalFormat::format(
275 UnicodeString
& appendTo
,
276 FieldPositionIterator
* /* posIter */,
277 UErrorCode
& status
) const {
278 status
= U_UNSUPPORTED_ERROR
;
283 CompactDecimalFormat::format(
285 UnicodeString
& appendTo
,
286 FieldPosition
& pos
) const {
287 return format((double) number
, appendTo
, pos
);
291 CompactDecimalFormat::format(
292 int64_t /* number */,
293 UnicodeString
& appendTo
,
294 FieldPositionIterator
* /* posIter */,
295 UErrorCode
& status
) const {
296 status
= U_UNSUPPORTED_ERROR
;
301 CompactDecimalFormat::format(
302 const StringPiece
& /* number */,
303 UnicodeString
& appendTo
,
304 FieldPositionIterator
* /* posIter */,
305 UErrorCode
& status
) const {
306 status
= U_UNSUPPORTED_ERROR
;
311 CompactDecimalFormat::format(
312 const DigitList
& /* number */,
313 UnicodeString
& appendTo
,
314 FieldPositionIterator
* /* posIter */,
315 UErrorCode
& status
) const {
316 status
= U_UNSUPPORTED_ERROR
;
321 CompactDecimalFormat::format(const DigitList
& /* number */,
322 UnicodeString
& appendTo
,
323 FieldPosition
& /* pos */,
324 UErrorCode
& status
) const {
325 status
= U_UNSUPPORTED_ERROR
;
330 CompactDecimalFormat::parse(
331 const UnicodeString
& /* text */,
332 Formattable
& /* result */,
333 ParsePosition
& /* parsePosition */) const {
337 CompactDecimalFormat::parse(
338 const UnicodeString
& /* text */,
339 Formattable
& /* result */,
340 UErrorCode
& status
) const {
341 status
= U_UNSUPPORTED_ERROR
;
345 CompactDecimalFormat::parseCurrency(
346 const UnicodeString
& /* text */,
347 ParsePosition
& /* pos */) const {
351 void CDFLocaleStyleData::Init(UErrorCode
& status
) {
352 if (unitsByVariant
!= NULL
) {
355 unitsByVariant
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
356 if (U_FAILURE(status
)) {
359 uhash_setKeyDeleter(unitsByVariant
, uprv_free
);
360 uhash_setValueDeleter(unitsByVariant
, deleteCDFUnits
);
363 CDFLocaleStyleData::~CDFLocaleStyleData() {
367 void CDFLocaleStyleData::setToBogus() {
368 if (unitsByVariant
!= NULL
) {
369 uhash_close(unitsByVariant
);
370 unitsByVariant
= NULL
;
374 void CDFLocaleData::Init(UErrorCode
& status
) {
375 shortData
.Init(status
);
376 if (U_FAILURE(status
)) {
379 longData
.Init(status
);
382 // Helper method for operator=
383 static UBool
divisors_equal(const double* lhs
, const double* rhs
) {
384 for (int32_t i
= 0; i
< MAX_DIGITS
; ++i
) {
385 if (lhs
[i
] != rhs
[i
]) {
392 // getCDFLocaleStyleData returns pointer to formatting data for given locale and
393 // style within the global cache. On cache miss, getCDFLocaleStyleData loads
394 // the data from CLDR into the global cache before returning the pointer. If a
395 // UNUM_LONG data is requested for a locale, and that locale does not have
396 // UNUM_LONG data, getCDFLocaleStyleData will fall back to UNUM_SHORT data for
398 static const CDFLocaleStyleData
* getCDFLocaleStyleData(const Locale
& inLocale
, UNumberCompactStyle style
, UErrorCode
& status
) {
399 if (U_FAILURE(status
)) {
402 CDFLocaleData
* result
= NULL
;
403 const char* key
= inLocale
.getName();
405 Mutex
lock(&gCompactDecimalMetaLock
);
406 if (gCompactDecimalData
== NULL
) {
407 gCompactDecimalData
= uhash_open(uhash_hashChars
, uhash_compareChars
, NULL
, &status
);
408 if (U_FAILURE(status
)) {
411 uhash_setKeyDeleter(gCompactDecimalData
, uprv_free
);
412 uhash_setValueDeleter(gCompactDecimalData
, deleteCDFLocaleData
);
413 ucln_i18n_registerCleanup(UCLN_I18N_CDFINFO
, cdf_cleanup
);
415 result
= (CDFLocaleData
*) uhash_get(gCompactDecimalData
, key
);
418 if (result
!= NULL
) {
419 return extractDataByStyleEnum(*result
, style
, status
);
422 result
= loadCDFLocaleData(inLocale
, status
);
423 if (U_FAILURE(status
)) {
428 Mutex
lock(&gCompactDecimalMetaLock
);
429 CDFLocaleData
* temp
= (CDFLocaleData
*) uhash_get(gCompactDecimalData
, key
);
434 uhash_put(gCompactDecimalData
, uprv_strdup(key
), (void*) result
, &status
);
435 if (U_FAILURE(status
)) {
440 return extractDataByStyleEnum(*result
, style
, status
);
443 static const CDFLocaleStyleData
* extractDataByStyleEnum(const CDFLocaleData
& data
, UNumberCompactStyle style
, UErrorCode
& status
) {
446 return &data
.shortData
;
448 if (!data
.longData
.isBogus()) {
449 return &data
.longData
;
451 return &data
.shortData
;
453 status
= U_ILLEGAL_ARGUMENT_ERROR
;
458 // loadCDFLocaleData loads formatting data from CLDR for a given locale. The
459 // caller owns the returned pointer.
460 static CDFLocaleData
* loadCDFLocaleData(const Locale
& inLocale
, UErrorCode
& status
) {
461 if (U_FAILURE(status
)) {
464 CDFLocaleData
* result
= new CDFLocaleData
;
465 if (result
== NULL
) {
466 status
= U_MEMORY_ALLOCATION_ERROR
;
469 result
->Init(status
);
470 if (U_FAILURE(status
)) {
475 initCDFLocaleData(inLocale
, result
, status
);
476 if (U_FAILURE(status
)) {
483 // initCDFLocaleData initializes result with data from CLDR.
484 // inLocale is the locale, the CLDR data is stored in result.
485 // We load the UNUM_SHORT and UNUM_LONG data looking first in local numbering
486 // system and not including root locale in fallback. Next we try in the latn
487 // numbering system where we fallback all the way to root. If we don't find
488 // UNUM_SHORT data in these three places, we report an error. If we find
489 // UNUM_SHORT data before finding UNUM_LONG data we make UNUM_LONG data fall
490 // back to UNUM_SHORT data.
491 static void initCDFLocaleData(const Locale
& inLocale
, CDFLocaleData
* result
, UErrorCode
& status
) {
492 LocalPointer
<NumberingSystem
> ns(NumberingSystem::createInstance(inLocale
, status
));
493 if (U_FAILURE(status
)) {
496 const char* numberingSystemName
= ns
->getName();
497 UResourceBundle
* rb
= ures_open(NULL
, inLocale
.getName(), &status
);
498 rb
= ures_getByKeyWithFallback(rb
, gNumberElementsTag
, rb
, &status
);
499 if (U_FAILURE(status
)) {
503 UResourceBundle
* shortDataFillIn
= NULL
;
504 UResourceBundle
* longDataFillIn
= NULL
;
505 UResourceBundle
* shortData
= NULL
;
506 UResourceBundle
* longData
= NULL
;
508 if (uprv_strcmp(numberingSystemName
, gLatnTag
) != 0) {
509 LocalUResourceBundlePointer
localResource(
510 tryGetByKeyWithFallback(rb
, numberingSystemName
, NULL
, NOT_ROOT
, status
));
511 shortData
= tryGetDecimalFallback(
512 localResource
.getAlias(), gPatternsShort
, &shortDataFillIn
, NOT_ROOT
, status
);
513 longData
= tryGetDecimalFallback(
514 localResource
.getAlias(), gPatternsLong
, &longDataFillIn
, NOT_ROOT
, status
);
516 if (U_FAILURE(status
)) {
517 ures_close(shortDataFillIn
);
518 ures_close(longDataFillIn
);
523 // If we haven't found UNUM_SHORT look in latn numbering system. We must
524 // succeed at finding UNUM_SHORT here.
525 if (shortData
== NULL
) {
526 LocalUResourceBundlePointer
latnResource(tryGetByKeyWithFallback(rb
, gLatnTag
, NULL
, MUST
, status
));
527 shortData
= tryGetDecimalFallback(latnResource
.getAlias(), gPatternsShort
, &shortDataFillIn
, MUST
, status
);
528 if (longData
== NULL
) {
529 longData
= tryGetDecimalFallback(latnResource
.getAlias(), gPatternsLong
, &longDataFillIn
, ANY
, status
);
530 if (longData
!= NULL
&& isRoot(longData
, status
) && !isRoot(shortData
, status
)) {
535 initCDFLocaleStyleData(shortData
, &result
->shortData
, status
);
536 ures_close(shortDataFillIn
);
537 if (U_FAILURE(status
)) {
538 ures_close(longDataFillIn
);
542 if (longData
== NULL
) {
543 result
->longData
.setToBogus();
545 initCDFLocaleStyleData(longData
, &result
->longData
, status
);
547 ures_close(longDataFillIn
);
552 * tryGetDecimalFallback attempts to fetch the "decimalFormat" resource bundle
553 * with a particular style. style is either "patternsShort" or "patternsLong."
554 * FillIn, flags, and status work in the same way as in tryGetByKeyWithFallback.
556 static UResourceBundle
* tryGetDecimalFallback(const UResourceBundle
* numberSystemResource
, const char* style
, UResourceBundle
** fillIn
, FallbackFlags flags
, UErrorCode
& status
) {
557 UResourceBundle
* first
= tryGetByKeyWithFallback(numberSystemResource
, style
, fillIn
, flags
, status
);
558 UResourceBundle
* second
= tryGetByKeyWithFallback(first
, gDecimalFormatTag
, fillIn
, flags
, status
);
559 if (fillIn
== NULL
) {
565 // tryGetByKeyWithFallback returns a sub-resource bundle that matches given
566 // criteria or NULL if none found. rb is the resource bundle that we are
567 // searching. If rb == NULL then this function behaves as if no sub-resource
568 // is found; path is the key of the sub-resource,
569 // (i.e "foo" but not "foo/bar"); If fillIn is NULL, caller must always call
570 // ures_close() on returned resource. See below for example when fillIn is
571 // not NULL. flags is ANY or NOT_ROOT. Optionally, these values
572 // can be ored with MUST. MUST by itself is the same as ANY | MUST.
573 // The locale of the returned sub-resource will either match the
574 // flags or the returned sub-resouce will be NULL. If MUST is included in
575 // flags, and not suitable sub-resource is found then in addition to returning
576 // NULL, this function also sets status to U_MISSING_RESOURCE_ERROR. If MUST
577 // is not included in flags, then this function just returns NULL if no
578 // such sub-resource is found and will never set status to
579 // U_MISSING_RESOURCE_ERROR.
581 // Example: This code first searches for "foo/bar" sub-resource without falling
582 // back to ROOT. Then searches for "baz" sub-resource as last resort.
584 // UResourcebundle* fillIn = NULL;
585 // UResourceBundle* data = tryGetByKeyWithFallback(rb, "foo", &fillIn, NON_ROOT, status);
586 // data = tryGetByKeyWithFallback(data, "bar", &fillIn, NON_ROOT, status);
588 // data = tryGetbyKeyWithFallback(rb, "baz", &fillIn, MUST, status);
590 // if (U_FAILURE(status)) {
591 // ures_close(fillIn);
594 // doStuffWithNonNullSubresource(data);
596 // /* Wrong! don't do the following as it can leak memory if fillIn gets set
598 // fillIn = tryGetByKeyWithFallback(rb, "wrong", &fillIn, ANY, status);
600 // ures_close(fillIn);
602 static UResourceBundle
* tryGetByKeyWithFallback(const UResourceBundle
* rb
, const char* path
, UResourceBundle
** fillIn
, FallbackFlags flags
, UErrorCode
& status
) {
603 if (U_FAILURE(status
)) {
606 UBool must
= (flags
& MUST
);
609 status
= U_MISSING_RESOURCE_ERROR
;
613 UResourceBundle
* result
= NULL
;
614 UResourceBundle
* ownedByUs
= NULL
;
615 if (fillIn
== NULL
) {
616 ownedByUs
= ures_getByKeyWithFallback(rb
, path
, NULL
, &status
);
619 *fillIn
= ures_getByKeyWithFallback(rb
, path
, *fillIn
, &status
);
622 if (U_FAILURE(status
)) {
623 ures_close(ownedByUs
);
624 if (status
== U_MISSING_RESOURCE_ERROR
&& !must
) {
625 status
= U_ZERO_ERROR
;
629 flags
= (FallbackFlags
) (flags
& ~MUST
);
633 UBool bRoot
= isRoot(result
, status
);
634 if (bRoot
|| U_FAILURE(status
)) {
635 ures_close(ownedByUs
);
636 if (must
&& (status
== U_ZERO_ERROR
)) {
637 status
= U_MISSING_RESOURCE_ERROR
;
646 ures_close(ownedByUs
);
647 status
= U_ILLEGAL_ARGUMENT_ERROR
;
652 static UBool
isRoot(const UResourceBundle
* rb
, UErrorCode
& status
) {
653 const char* actualLocale
= ures_getLocaleByType(
654 rb
, ULOC_ACTUAL_LOCALE
, &status
);
655 if (U_FAILURE(status
)) {
658 return uprv_strcmp(actualLocale
, gRoot
) == 0;
662 // initCDFLocaleStyleData loads formatting data for a particular style.
663 // decimalFormatBundle is the "decimalFormat" resource bundle in CLDR.
664 // Loaded data stored in result.
665 static void initCDFLocaleStyleData(const UResourceBundle
* decimalFormatBundle
, CDFLocaleStyleData
* result
, UErrorCode
& status
) {
666 if (U_FAILURE(status
)) {
669 // Iterate through all the powers of 10.
670 int32_t size
= ures_getSize(decimalFormatBundle
);
671 UResourceBundle
* power10
= NULL
;
672 for (int32_t i
= 0; i
< size
; ++i
) {
673 power10
= ures_getByIndex(decimalFormatBundle
, i
, power10
, &status
);
674 if (U_FAILURE(status
)) {
678 populatePower10(power10
, result
, status
);
679 if (U_FAILURE(status
)) {
685 fillInMissing(result
);
688 // populatePower10 grabs data for a particular power of 10 from CLDR.
689 // The loaded data is stored in result.
690 static void populatePower10(const UResourceBundle
* power10Bundle
, CDFLocaleStyleData
* result
, UErrorCode
& status
) {
691 if (U_FAILURE(status
)) {
695 double power10
= uprv_strtod(ures_getKey(power10Bundle
), &endPtr
);
697 status
= U_INTERNAL_PROGRAM_ERROR
;
700 int32_t log10Value
= computeLog10(power10
, FALSE
);
701 // Silently ignore divisors that are too big.
702 if (log10Value
== MAX_DIGITS
) {
705 int32_t size
= ures_getSize(power10Bundle
);
706 int32_t numZeros
= 0;
707 UBool otherVariantDefined
= FALSE
;
708 UResourceBundle
* variantBundle
= NULL
;
709 // Iterate over all the plural variants for the power of 10
710 for (int32_t i
= 0; i
< size
; ++i
) {
711 variantBundle
= ures_getByIndex(power10Bundle
, i
, variantBundle
, &status
);
712 if (U_FAILURE(status
)) {
713 ures_close(variantBundle
);
716 const char* variant
= ures_getKey(variantBundle
);
718 const UChar
* formatStrP
= ures_getString(variantBundle
, &resLen
, &status
);
719 if (U_FAILURE(status
)) {
720 ures_close(variantBundle
);
723 UnicodeString
formatStr(false, formatStrP
, resLen
);
724 if (uprv_strcmp(variant
, gOther
) == 0) {
725 otherVariantDefined
= TRUE
;
727 int32_t nz
= populatePrefixSuffix(
728 variant
, log10Value
, formatStr
, result
->unitsByVariant
, status
);
729 if (U_FAILURE(status
)) {
730 ures_close(variantBundle
);
733 if (nz
!= numZeros
) {
734 // We expect all format strings to have the same number of 0's
735 // left of the decimal point.
737 status
= U_INTERNAL_PROGRAM_ERROR
;
738 ures_close(variantBundle
);
744 ures_close(variantBundle
);
745 // We expect to find an OTHER variant for each power of 10.
746 if (!otherVariantDefined
) {
747 status
= U_INTERNAL_PROGRAM_ERROR
;
750 double divisor
= power10
;
751 for (int32_t i
= 1; i
< numZeros
; ++i
) {
754 result
->divisors
[log10Value
] = divisor
;
757 // populatePrefixSuffix Adds a specific prefix-suffix pair to result for a
758 // given variant and log10 value.
759 // variant is 'zero', 'one', 'two', 'few', 'many', or 'other'.
760 // formatStr is the format string from which the prefix and suffix are
761 // extracted. It is usually of form 'Pefix 000 suffix'.
762 // populatePrefixSuffix returns the number of 0's found in formatStr
763 // before the decimal point.
764 // In the special case that formatStr contains only spaces for prefix
765 // and suffix, populatePrefixSuffix returns log10Value + 1.
766 static int32_t populatePrefixSuffix(
767 const char* variant
, int32_t log10Value
, const UnicodeString
& formatStr
, UHashtable
* result
, UErrorCode
& status
) {
768 if (U_FAILURE(status
)) {
771 int32_t firstIdx
= formatStr
.indexOf(kZero
, LENGTHOF(kZero
), 0);
772 // We must have 0's in format string.
773 if (firstIdx
== -1) {
774 status
= U_INTERNAL_PROGRAM_ERROR
;
777 int32_t lastIdx
= formatStr
.lastIndexOf(kZero
, LENGTHOF(kZero
), firstIdx
);
778 CDFUnit
* unit
= createCDFUnit(variant
, log10Value
, result
, status
);
779 if (U_FAILURE(status
)) {
782 // Everything up to first 0 is the prefix
783 unit
->prefix
= formatStr
.tempSubString(0, firstIdx
);
784 fixQuotes(unit
->prefix
);
785 // Everything beyond the last 0 is the suffix
786 unit
->suffix
= formatStr
.tempSubString(lastIdx
+ 1);
787 fixQuotes(unit
->suffix
);
789 // If there is effectively no prefix or suffix, ignore the actual number of
790 // 0's and act as if the number of 0's matches the size of the number.
791 if (onlySpaces(unit
->prefix
) && onlySpaces(unit
->suffix
)) {
792 return log10Value
+ 1;
795 // Calculate number of zeros before decimal point
796 int32_t idx
= firstIdx
+ 1;
797 while (idx
<= lastIdx
&& formatStr
.charAt(idx
) == u_0
) {
800 return (idx
- firstIdx
);
803 static UBool
onlySpaces(UnicodeString u
) {
804 return u
.trim().length() == 0;
807 // fixQuotes unescapes single quotes. Don''t -> Don't. Letter 'j' -> Letter j.
808 // Modifies s in place.
809 static void fixQuotes(UnicodeString
& s
) {
810 QuoteState state
= OUTSIDE
;
811 int32_t len
= s
.length();
813 for (int32_t i
= 0; i
< len
; ++i
) {
814 UChar ch
= s
.charAt(i
);
816 if (state
== INSIDE_EMPTY
) {
817 s
.setCharAt(dest
, ch
);
821 s
.setCharAt(dest
, ch
);
828 state
= ch
== u_apos
? INSIDE_EMPTY
: OUTSIDE
;
832 state
= ch
== u_apos
? OUTSIDE
: INSIDE_FULL
;
841 // fillInMissing ensures that the data in result is complete.
842 // result data is complete if for each variant in result, there exists
843 // a prefix-suffix pair for each log10 value and there also exists
844 // a divisor for each log10 value.
846 // First this function figures out for which log10 values, the other
847 // variant already had data. These are the same log10 values defined
850 // For each log10 value not defined in CLDR, it uses the divisor for
851 // the last defined log10 value or 1.
853 // Then for each variant, it does the following. For each log10
854 // value not defined in CLDR, copy the prefix-suffix pair from the
855 // previous log10 value. If log10 value is defined in CLDR but is
856 // missing from given variant, copy the prefix-suffix pair for that
857 // log10 value from the 'other' variant.
858 static void fillInMissing(CDFLocaleStyleData
* result
) {
859 const CDFUnit
* otherUnits
=
860 (const CDFUnit
*) uhash_get(result
->unitsByVariant
, gOther
);
861 UBool definedInCLDR
[MAX_DIGITS
];
862 double lastDivisor
= 1.0;
863 for (int32_t i
= 0; i
< MAX_DIGITS
; ++i
) {
864 if (!otherUnits
[i
].isSet()) {
865 result
->divisors
[i
] = lastDivisor
;
866 definedInCLDR
[i
] = FALSE
;
868 lastDivisor
= result
->divisors
[i
];
869 definedInCLDR
[i
] = TRUE
;
872 // Iterate over each variant.
874 const UHashElement
* element
= uhash_nextElement(result
->unitsByVariant
, &pos
);
875 for (;element
!= NULL
; element
= uhash_nextElement(result
->unitsByVariant
, &pos
)) {
876 CDFUnit
* units
= (CDFUnit
*) element
->value
.pointer
;
877 for (int32_t i
= 0; i
< MAX_DIGITS
; ++i
) {
878 if (definedInCLDR
[i
]) {
879 if (!units
[i
].isSet()) {
880 units
[i
] = otherUnits
[i
];
884 units
[0].markAsSet();
886 units
[i
] = units
[i
- 1];
893 // computeLog10 computes floor(log10(x)). If inRange is TRUE, the biggest
894 // value computeLog10 will return MAX_DIGITS -1 even for
895 // numbers > 10^MAX_DIGITS. If inRange is FALSE, computeLog10 will return
897 static int32_t computeLog10(double x
, UBool inRange
) {
899 int32_t max
= inRange
? MAX_DIGITS
- 1 : MAX_DIGITS
;
910 // createCDFUnit returns a pointer to the prefix-suffix pair for a given
911 // variant and log10 value within table. If no such prefix-suffix pair is
912 // stored in table, one is created within table before returning pointer.
913 static CDFUnit
* createCDFUnit(const char* variant
, int32_t log10Value
, UHashtable
* table
, UErrorCode
& status
) {
914 if (U_FAILURE(status
)) {
917 CDFUnit
*cdfUnit
= (CDFUnit
*) uhash_get(table
, variant
);
918 if (cdfUnit
== NULL
) {
919 cdfUnit
= new CDFUnit
[MAX_DIGITS
];
920 if (cdfUnit
== NULL
) {
921 status
= U_MEMORY_ALLOCATION_ERROR
;
924 uhash_put(table
, uprv_strdup(variant
), cdfUnit
, &status
);
925 if (U_FAILURE(status
)) {
929 CDFUnit
* result
= &cdfUnit
[log10Value
];
934 // getCDFUnitFallback returns a pointer to the prefix-suffix pair for a given
935 // variant and log10 value within table. If the given variant doesn't exist, it
936 // falls back to the OTHER variant. Therefore, this method will always return
937 // some non-NULL value.
938 static const CDFUnit
* getCDFUnitFallback(const UHashtable
* table
, const UnicodeString
& variant
, int32_t log10Value
) {
940 UErrorCode status
= U_ZERO_ERROR
;
941 const CDFUnit
*cdfUnit
= NULL
;
942 cvariant
.appendInvariantChars(variant
, status
);
943 if (!U_FAILURE(status
)) {
944 cdfUnit
= (const CDFUnit
*) uhash_get(table
, cvariant
.data());
946 if (cdfUnit
== NULL
) {
947 cdfUnit
= (const CDFUnit
*) uhash_get(table
, gOther
);
949 return &cdfUnit
[log10Value
];