]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/compactdecimalformat.cpp
ICU-551.51.4.tar.gz
[apple/icu.git] / icuSources / i18n / compactdecimalformat.cpp
CommitLineData
51004dcb
A
1/*
2*******************************************************************************
b331163b 3* Copyright (C) 1997-2014, International Business Machines Corporation and *
51004dcb
A
4* others. All Rights Reserved. *
5*******************************************************************************
6*
7* File COMPACTDECIMALFORMAT.CPP
8*
9********************************************************************************
10*/
11#include "unicode/utypes.h"
12
13#if !UCONFIG_NO_FORMATTING
14
15#include "charstr.h"
16#include "cstring.h"
17#include "digitlst.h"
18#include "mutex.h"
19#include "unicode/compactdecimalformat.h"
20#include "unicode/numsys.h"
21#include "unicode/plurrule.h"
22#include "unicode/ures.h"
23#include "ucln_in.h"
24#include "uhash.h"
25#include "umutex.h"
26#include "unicode/ures.h"
27#include "uresimp.h"
28
51004dcb
A
29// Maps locale name to CDFLocaleData struct.
30static UHashtable* gCompactDecimalData = NULL;
31static UMutex gCompactDecimalMetaLock = U_MUTEX_INITIALIZER;
32
33U_NAMESPACE_BEGIN
34
35static const int32_t MAX_DIGITS = 15;
36static const char gOther[] = "other";
37static const char gLatnTag[] = "latn";
38static const char gNumberElementsTag[] = "NumberElements";
39static const char gDecimalFormatTag[] = "decimalFormat";
40static const char gPatternsShort[] = "patternsShort";
41static const char gPatternsLong[] = "patternsLong";
42static const char gRoot[] = "root";
43
44static const UChar u_0 = 0x30;
45static const UChar u_apos = 0x27;
46
47static const UChar kZero[] = {u_0};
48
49// Used to unescape single quotes.
50enum QuoteState {
51 OUTSIDE,
52 INSIDE_EMPTY,
53 INSIDE_FULL
54};
55
56enum FallbackFlags {
57 ANY = 0,
58 MUST = 1,
59 NOT_ROOT = 2
60 // Next one will be 4 then 6 etc.
61};
62
63
64// CDFUnit represents a prefix-suffix pair for a particular variant
65// and log10 value.
66struct CDFUnit : public UMemory {
67 UnicodeString prefix;
68 UnicodeString suffix;
69 inline CDFUnit() : prefix(), suffix() {
70 prefix.setToBogus();
71 }
72 inline ~CDFUnit() {}
73 inline UBool isSet() const {
74 return !prefix.isBogus();
75 }
76 inline void markAsSet() {
77 prefix.remove();
78 }
79};
80
81// CDFLocaleStyleData contains formatting data for a particular locale
82// and style.
83class CDFLocaleStyleData : public UMemory {
84 public:
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;
103 }
104 void setToBogus();
105 private:
106 CDFLocaleStyleData(const CDFLocaleStyleData&);
107 CDFLocaleStyleData& operator=(const CDFLocaleStyleData&);
108};
109
110// CDFLocaleData contains formatting data for a particular locale.
111struct 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);
118};
119
120U_NAMESPACE_END
121
122U_CDECL_BEGIN
123
124static UBool U_CALLCONV cdf_cleanup(void) {
125 if (gCompactDecimalData != NULL) {
126 uhash_close(gCompactDecimalData);
127 gCompactDecimalData = NULL;
128 }
129 return TRUE;
130}
131
132static void U_CALLCONV deleteCDFUnits(void* ptr) {
133 delete [] (icu::CDFUnit*) ptr;
134}
135
136static void U_CALLCONV deleteCDFLocaleData(void* ptr) {
137 delete (icu::CDFLocaleData*) ptr;
138}
139
140U_CDECL_END
141
142U_NAMESPACE_BEGIN
143
144static UBool divisors_equal(const double* lhs, const double* rhs);
145static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status);
146
147static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status);
148static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status);
149static void initCDFLocaleData(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status);
150static UResourceBundle* tryGetDecimalFallback(const UResourceBundle* numberSystemResource, const char* style, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status);
151static UResourceBundle* tryGetByKeyWithFallback(const UResourceBundle* rb, const char* path, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status);
152static UBool isRoot(const UResourceBundle* rb, UErrorCode& status);
153static void initCDFLocaleStyleData(const UResourceBundle* decimalFormatBundle, CDFLocaleStyleData* result, UErrorCode& status);
154static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyleData* result, UErrorCode& status);
155static int32_t populatePrefixSuffix(const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UErrorCode& status);
156static UBool onlySpaces(UnicodeString u);
157static void fixQuotes(UnicodeString& s);
158static void fillInMissing(CDFLocaleStyleData* result);
159static int32_t computeLog10(double x, UBool inRange);
160static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status);
161static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value);
162
163UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CompactDecimalFormat)
164
165CompactDecimalFormat::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) {
171}
172
173CompactDecimalFormat::CompactDecimalFormat(const CompactDecimalFormat& source)
174 : DecimalFormat(source), _unitsByVariant(source._unitsByVariant), _divisors(source._divisors), _pluralRules(source._pluralRules->clone()) {
175}
176
177CompactDecimalFormat* U_EXPORT2
178CompactDecimalFormat::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)) {
182 return NULL;
183 }
184 LocalPointer<PluralRules> pluralRules(PluralRules::forLocale(inLocale, status));
185 if (U_FAILURE(status)) {
186 return NULL;
187 }
188 const CDFLocaleStyleData* data = getCDFLocaleStyleData(inLocale, style, status);
189 if (U_FAILURE(status)) {
190 return NULL;
191 }
192 CompactDecimalFormat* result =
193 new CompactDecimalFormat(*decfmt, data->unitsByVariant, data->divisors, pluralRules.getAlias());
194 if (result == NULL) {
195 status = U_MEMORY_ALLOCATION_ERROR;
196 return NULL;
197 }
198 pluralRules.orphan();
199 result->setMaximumSignificantDigits(3);
200 result->setSignificantDigitsUsed(TRUE);
201 result->setGroupingUsed(FALSE);
202 return result;
203}
204
205CompactDecimalFormat&
206CompactDecimalFormat::operator=(const CompactDecimalFormat& rhs) {
207 if (this != &rhs) {
208 DecimalFormat::operator=(rhs);
209 _unitsByVariant = rhs._unitsByVariant;
210 _divisors = rhs._divisors;
211 delete _pluralRules;
212 _pluralRules = rhs._pluralRules->clone();
213 }
214 return *this;
215}
216
217CompactDecimalFormat::~CompactDecimalFormat() {
218 delete _pluralRules;
219}
220
221
222Format*
223CompactDecimalFormat::clone(void) const {
224 return new CompactDecimalFormat(*this);
225}
226
227UBool
228CompactDecimalFormat::operator==(const Format& that) const {
229 if (this == &that) {
230 return TRUE;
231 }
232 return (DecimalFormat::operator==(that) && eqHelper((const CompactDecimalFormat&) that));
233}
234
235UBool
236CompactDecimalFormat::eqHelper(const CompactDecimalFormat& that) const {
237 return uhash_equals(_unitsByVariant, that._unitsByVariant) && divisors_equal(_divisors, that._divisors) && (*_pluralRules == *that._pluralRules);
238}
239
240UnicodeString&
241CompactDecimalFormat::format(
242 double number,
243 UnicodeString& appendTo,
244 FieldPosition& pos) const {
245 DigitList orig, rounded;
246 orig.set(number);
247 UBool isNegative;
248 UErrorCode status = U_ZERO_ERROR;
249 _round(orig, rounded, isNegative, status);
250 if (U_FAILURE(status)) {
251 return appendTo;
252 }
253 double roundedDouble = rounded.getDouble();
254 if (isNegative) {
255 roundedDouble = -roundedDouble;
256 }
257 int32_t baseIdx = computeLog10(roundedDouble, TRUE);
258 double numberToFormat = roundedDouble / _divisors[baseIdx];
259 UnicodeString variant = _pluralRules->select(numberToFormat);
260 if (isNegative) {
261 numberToFormat = -numberToFormat;
262 }
263 const CDFUnit* unit = getCDFUnitFallback(_unitsByVariant, variant, baseIdx);
264 appendTo += unit->prefix;
265 DecimalFormat::format(numberToFormat, appendTo, pos);
266 appendTo += unit->suffix;
267 return appendTo;
268}
269
270UnicodeString&
271CompactDecimalFormat::format(
272 double /* number */,
273 UnicodeString& appendTo,
274 FieldPositionIterator* /* posIter */,
275 UErrorCode& status) const {
276 status = U_UNSUPPORTED_ERROR;
277 return appendTo;
278}
279
280UnicodeString&
281CompactDecimalFormat::format(
282 int64_t number,
283 UnicodeString& appendTo,
284 FieldPosition& pos) const {
285 return format((double) number, appendTo, pos);
286}
287
288UnicodeString&
289CompactDecimalFormat::format(
290 int64_t /* number */,
291 UnicodeString& appendTo,
292 FieldPositionIterator* /* posIter */,
293 UErrorCode& status) const {
294 status = U_UNSUPPORTED_ERROR;
295 return appendTo;
296}
297
298UnicodeString&
299CompactDecimalFormat::format(
300 const StringPiece& /* number */,
301 UnicodeString& appendTo,
302 FieldPositionIterator* /* posIter */,
303 UErrorCode& status) const {
304 status = U_UNSUPPORTED_ERROR;
305 return appendTo;
306}
307
308UnicodeString&
309CompactDecimalFormat::format(
310 const DigitList& /* number */,
311 UnicodeString& appendTo,
312 FieldPositionIterator* /* posIter */,
313 UErrorCode& status) const {
314 status = U_UNSUPPORTED_ERROR;
315 return appendTo;
316}
317
318UnicodeString&
319CompactDecimalFormat::format(const DigitList& /* number */,
320 UnicodeString& appendTo,
321 FieldPosition& /* pos */,
322 UErrorCode& status) const {
323 status = U_UNSUPPORTED_ERROR;
324 return appendTo;
325}
326
327void
328CompactDecimalFormat::parse(
329 const UnicodeString& /* text */,
330 Formattable& /* result */,
331 ParsePosition& /* parsePosition */) const {
332}
333
334void
335CompactDecimalFormat::parse(
336 const UnicodeString& /* text */,
337 Formattable& /* result */,
338 UErrorCode& status) const {
339 status = U_UNSUPPORTED_ERROR;
340}
341
342CurrencyAmount*
343CompactDecimalFormat::parseCurrency(
344 const UnicodeString& /* text */,
345 ParsePosition& /* pos */) const {
346 return NULL;
347}
348
349void CDFLocaleStyleData::Init(UErrorCode& status) {
350 if (unitsByVariant != NULL) {
351 return;
352 }
353 unitsByVariant = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
354 if (U_FAILURE(status)) {
355 return;
356 }
357 uhash_setKeyDeleter(unitsByVariant, uprv_free);
358 uhash_setValueDeleter(unitsByVariant, deleteCDFUnits);
359}
360
361CDFLocaleStyleData::~CDFLocaleStyleData() {
362 setToBogus();
363}
364
365void CDFLocaleStyleData::setToBogus() {
366 if (unitsByVariant != NULL) {
367 uhash_close(unitsByVariant);
368 unitsByVariant = NULL;
369 }
370}
371
372void CDFLocaleData::Init(UErrorCode& status) {
373 shortData.Init(status);
374 if (U_FAILURE(status)) {
375 return;
376 }
377 longData.Init(status);
378}
379
380// Helper method for operator=
381static 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]) {
384 return FALSE;
385 }
386 }
387 return TRUE;
388}
389
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
395// that locale.
396static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, UNumberCompactStyle style, UErrorCode& status) {
397 if (U_FAILURE(status)) {
398 return NULL;
399 }
400 CDFLocaleData* result = NULL;
401 const char* key = inLocale.getName();
402 {
403 Mutex lock(&gCompactDecimalMetaLock);
404 if (gCompactDecimalData == NULL) {
405 gCompactDecimalData = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
406 if (U_FAILURE(status)) {
407 return NULL;
408 }
409 uhash_setKeyDeleter(gCompactDecimalData, uprv_free);
410 uhash_setValueDeleter(gCompactDecimalData, deleteCDFLocaleData);
411 ucln_i18n_registerCleanup(UCLN_I18N_CDFINFO, cdf_cleanup);
412 } else {
413 result = (CDFLocaleData*) uhash_get(gCompactDecimalData, key);
414 }
415 }
416 if (result != NULL) {
417 return extractDataByStyleEnum(*result, style, status);
418 }
419
420 result = loadCDFLocaleData(inLocale, status);
421 if (U_FAILURE(status)) {
422 return NULL;
423 }
424
425 {
426 Mutex lock(&gCompactDecimalMetaLock);
427 CDFLocaleData* temp = (CDFLocaleData*) uhash_get(gCompactDecimalData, key);
428 if (temp != NULL) {
429 delete result;
430 result = temp;
431 } else {
432 uhash_put(gCompactDecimalData, uprv_strdup(key), (void*) result, &status);
433 if (U_FAILURE(status)) {
434 return NULL;
435 }
436 }
437 }
438 return extractDataByStyleEnum(*result, style, status);
439}
440
441static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status) {
442 switch (style) {
443 case UNUM_SHORT:
444 return &data.shortData;
445 case UNUM_LONG:
446 if (!data.longData.isBogus()) {
447 return &data.longData;
448 }
449 return &data.shortData;
450 default:
451 status = U_ILLEGAL_ARGUMENT_ERROR;
452 return NULL;
453 }
454}
455
456// loadCDFLocaleData loads formatting data from CLDR for a given locale. The
457// caller owns the returned pointer.
458static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status) {
459 if (U_FAILURE(status)) {
460 return NULL;
461 }
462 CDFLocaleData* result = new CDFLocaleData;
463 if (result == NULL) {
464 status = U_MEMORY_ALLOCATION_ERROR;
465 return NULL;
466 }
467 result->Init(status);
468 if (U_FAILURE(status)) {
469 delete result;
470 return NULL;
471 }
472
473 initCDFLocaleData(inLocale, result, status);
474 if (U_FAILURE(status)) {
475 delete result;
476 return NULL;
477 }
478 return result;
479}
480
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.
489static void initCDFLocaleData(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status) {
490 LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(inLocale, status));
491 if (U_FAILURE(status)) {
492 return;
493 }
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)) {
498 ures_close(rb);
499 return;
500 }
501 UResourceBundle* shortDataFillIn = NULL;
502 UResourceBundle* longDataFillIn = NULL;
503 UResourceBundle* shortData = NULL;
504 UResourceBundle* longData = NULL;
505
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);
513 }
514 if (U_FAILURE(status)) {
515 ures_close(shortDataFillIn);
516 ures_close(longDataFillIn);
517 ures_close(rb);
518 return;
519 }
520
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)) {
529 longData = NULL;
530 }
531 }
532 }
533 initCDFLocaleStyleData(shortData, &result->shortData, status);
534 ures_close(shortDataFillIn);
535 if (U_FAILURE(status)) {
536 ures_close(longDataFillIn);
537 ures_close(rb);
538 }
539
540 if (longData == NULL) {
541 result->longData.setToBogus();
542 } else {
543 initCDFLocaleStyleData(longData, &result->longData, status);
544 }
545 ures_close(longDataFillIn);
546 ures_close(rb);
547}
548
549/**
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.
553 */
554static 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) {
558 ures_close(first);
559 }
560 return second;
561}
562
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.
578//
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.
581//
582// UResourcebundle* fillIn = NULL;
583// UResourceBundle* data = tryGetByKeyWithFallback(rb, "foo", &fillIn, NON_ROOT, status);
584// data = tryGetByKeyWithFallback(data, "bar", &fillIn, NON_ROOT, status);
585// if (!data) {
586// data = tryGetbyKeyWithFallback(rb, "baz", &fillIn, MUST, status);
587// }
588// if (U_FAILURE(status)) {
589// ures_close(fillIn);
590// return;
591// }
592// doStuffWithNonNullSubresource(data);
593//
594// /* Wrong! don't do the following as it can leak memory if fillIn gets set
595// to NULL. */
596// fillIn = tryGetByKeyWithFallback(rb, "wrong", &fillIn, ANY, status);
597//
598// ures_close(fillIn);
599//
600static UResourceBundle* tryGetByKeyWithFallback(const UResourceBundle* rb, const char* path, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status) {
601 if (U_FAILURE(status)) {
602 return NULL;
603 }
604 UBool must = (flags & MUST);
605 if (rb == NULL) {
606 if (must) {
607 status = U_MISSING_RESOURCE_ERROR;
608 }
609 return NULL;
610 }
611 UResourceBundle* result = NULL;
612 UResourceBundle* ownedByUs = NULL;
613 if (fillIn == NULL) {
614 ownedByUs = ures_getByKeyWithFallback(rb, path, NULL, &status);
615 result = ownedByUs;
616 } else {
617 *fillIn = ures_getByKeyWithFallback(rb, path, *fillIn, &status);
618 result = *fillIn;
619 }
620 if (U_FAILURE(status)) {
621 ures_close(ownedByUs);
622 if (status == U_MISSING_RESOURCE_ERROR && !must) {
623 status = U_ZERO_ERROR;
624 }
625 return NULL;
626 }
627 flags = (FallbackFlags) (flags & ~MUST);
628 switch (flags) {
629 case NOT_ROOT:
630 {
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;
636 }
637 return NULL;
638 }
639 return result;
640 }
641 case ANY:
642 return result;
643 default:
644 ures_close(ownedByUs);
645 status = U_ILLEGAL_ARGUMENT_ERROR;
646 return NULL;
647 }
648}
649
650static UBool isRoot(const UResourceBundle* rb, UErrorCode& status) {
651 const char* actualLocale = ures_getLocaleByType(
652 rb, ULOC_ACTUAL_LOCALE, &status);
653 if (U_FAILURE(status)) {
654 return FALSE;
655 }
656 return uprv_strcmp(actualLocale, gRoot) == 0;
657}
658
659
660// initCDFLocaleStyleData loads formatting data for a particular style.
661// decimalFormatBundle is the "decimalFormat" resource bundle in CLDR.
662// Loaded data stored in result.
663static void initCDFLocaleStyleData(const UResourceBundle* decimalFormatBundle, CDFLocaleStyleData* result, UErrorCode& status) {
664 if (U_FAILURE(status)) {
665 return;
666 }
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)) {
673 ures_close(power10);
674 return;
675 }
676 populatePower10(power10, result, status);
677 if (U_FAILURE(status)) {
678 ures_close(power10);
679 return;
680 }
681 }
682 ures_close(power10);
683 fillInMissing(result);
684}
685
686// populatePower10 grabs data for a particular power of 10 from CLDR.
687// The loaded data is stored in result.
688static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyleData* result, UErrorCode& status) {
689 if (U_FAILURE(status)) {
690 return;
691 }
692 char* endPtr = NULL;
693 double power10 = uprv_strtod(ures_getKey(power10Bundle), &endPtr);
694 if (*endPtr != 0) {
695 status = U_INTERNAL_PROGRAM_ERROR;
696 return;
697 }
698 int32_t log10Value = computeLog10(power10, FALSE);
699 // Silently ignore divisors that are too big.
700 if (log10Value == MAX_DIGITS) {
701 return;
702 }
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);
712 return;
713 }
714 const char* variant = ures_getKey(variantBundle);
715 int32_t resLen;
716 const UChar* formatStrP = ures_getString(variantBundle, &resLen, &status);
717 if (U_FAILURE(status)) {
718 ures_close(variantBundle);
719 return;
720 }
721 UnicodeString formatStr(false, formatStrP, resLen);
722 if (uprv_strcmp(variant, gOther) == 0) {
723 otherVariantDefined = TRUE;
724 }
725 int32_t nz = populatePrefixSuffix(
726 variant, log10Value, formatStr, result->unitsByVariant, status);
727 if (U_FAILURE(status)) {
728 ures_close(variantBundle);
729 return;
730 }
731 if (nz != numZeros) {
732 // We expect all format strings to have the same number of 0's
733 // left of the decimal point.
734 if (numZeros != 0) {
735 status = U_INTERNAL_PROGRAM_ERROR;
736 ures_close(variantBundle);
737 return;
738 }
739 numZeros = nz;
740 }
741 }
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;
746 return;
747 }
748 double divisor = power10;
749 for (int32_t i = 1; i < numZeros; ++i) {
750 divisor /= 10.0;
751 }
752 result->divisors[log10Value] = divisor;
753}
754
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.
764static int32_t populatePrefixSuffix(
765 const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UErrorCode& status) {
766 if (U_FAILURE(status)) {
767 return 0;
768 }
b331163b 769 int32_t firstIdx = formatStr.indexOf(kZero, UPRV_LENGTHOF(kZero), 0);
51004dcb
A
770 // We must have 0's in format string.
771 if (firstIdx == -1) {
772 status = U_INTERNAL_PROGRAM_ERROR;
773 return 0;
774 }
b331163b 775 int32_t lastIdx = formatStr.lastIndexOf(kZero, UPRV_LENGTHOF(kZero), firstIdx);
51004dcb
A
776 CDFUnit* unit = createCDFUnit(variant, log10Value, result, status);
777 if (U_FAILURE(status)) {
778 return 0;
779 }
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);
786
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;
791 }
792
793 // Calculate number of zeros before decimal point
794 int32_t idx = firstIdx + 1;
795 while (idx <= lastIdx && formatStr.charAt(idx) == u_0) {
796 ++idx;
797 }
798 return (idx - firstIdx);
799}
800
801static UBool onlySpaces(UnicodeString u) {
802 return u.trim().length() == 0;
803}
804
805// fixQuotes unescapes single quotes. Don''t -> Don't. Letter 'j' -> Letter j.
806// Modifies s in place.
807static void fixQuotes(UnicodeString& s) {
808 QuoteState state = OUTSIDE;
809 int32_t len = s.length();
810 int32_t dest = 0;
811 for (int32_t i = 0; i < len; ++i) {
812 UChar ch = s.charAt(i);
813 if (ch == u_apos) {
814 if (state == INSIDE_EMPTY) {
815 s.setCharAt(dest, ch);
816 ++dest;
817 }
818 } else {
819 s.setCharAt(dest, ch);
820 ++dest;
821 }
822
823 // Update state
824 switch (state) {
825 case OUTSIDE:
826 state = ch == u_apos ? INSIDE_EMPTY : OUTSIDE;
827 break;
828 case INSIDE_EMPTY:
829 case INSIDE_FULL:
830 state = ch == u_apos ? OUTSIDE : INSIDE_FULL;
831 break;
832 default:
833 break;
834 }
835 }
836 s.truncate(dest);
837}
838
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.
843//
844// First this function figures out for which log10 values, the other
845// variant already had data. These are the same log10 values defined
846// in CLDR.
847//
848// For each log10 value not defined in CLDR, it uses the divisor for
849// the last defined log10 value or 1.
850//
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.
856static 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;
865 } else {
866 lastDivisor = result->divisors[i];
867 definedInCLDR[i] = TRUE;
868 }
869 }
870 // Iterate over each variant.
b331163b 871 int32_t pos = UHASH_FIRST;
51004dcb
A
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];
879 }
880 } else {
881 if (i == 0) {
882 units[0].markAsSet();
883 } else {
884 units[i] = units[i - 1];
885 }
886 }
887 }
888 }
889}
890
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
894// up to MAX_DIGITS.
895static int32_t computeLog10(double x, UBool inRange) {
896 int32_t result = 0;
897 int32_t max = inRange ? MAX_DIGITS - 1 : MAX_DIGITS;
898 while (x >= 10.0) {
899 x /= 10.0;
900 ++result;
901 if (result == max) {
902 break;
903 }
904 }
905 return result;
906}
907
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.
911static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status) {
912 if (U_FAILURE(status)) {
913 return NULL;
914 }
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;
920 return NULL;
921 }
922 uhash_put(table, uprv_strdup(variant), cdfUnit, &status);
923 if (U_FAILURE(status)) {
924 return NULL;
925 }
926 }
927 CDFUnit* result = &cdfUnit[log10Value];
928 result->markAsSet();
929 return result;
930}
931
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.
936static const CDFUnit* getCDFUnitFallback(const UHashtable* table, const UnicodeString& variant, int32_t log10Value) {
937 CharString cvariant;
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());
943 }
944 if (cdfUnit == NULL) {
945 cdfUnit = (const CDFUnit*) uhash_get(table, gOther);
946 }
947 return &cdfUnit[log10Value];
948}
949
950U_NAMESPACE_END
951#endif