compactdecimalformat.cpp revision 1b7d32f919554dda9c193b32188251337bc756f1
1/*
2*******************************************************************************
3* Copyright (C) 1997-2014, International Business Machines Corporation and    *
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
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  }
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;
773    return 0;
774  }
775  int32_t lastIdx = formatStr.lastIndexOf(kZero, UPRV_LENGTHOF(kZero), firstIdx);
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.
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];
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
952