Index: source/i18n/compactdecimalformat.cpp |
diff --git a/source/i18n/compactdecimalformat.cpp b/source/i18n/compactdecimalformat.cpp |
index 2e5054775c22e781351d5013b08f0f81fb4d374c..385b3a513f263254323b31d5080871a261463745 100644 |
--- a/source/i18n/compactdecimalformat.cpp |
+++ b/source/i18n/compactdecimalformat.cpp |
@@ -1,3 +1,5 @@ |
+// Copyright (C) 2016 and later: Unicode, Inc. and others. |
+// License & terms of use: http://www.unicode.org/copyright.html |
/* |
******************************************************************************* |
* Copyright (C) 1997-2015, International Business Machines Corporation and * |
@@ -39,7 +41,7 @@ static const char gNumberElementsTag[] = "NumberElements"; |
static const char gDecimalFormatTag[] = "decimalFormat"; |
static const char gPatternsShort[] = "patternsShort"; |
static const char gPatternsLong[] = "patternsLong"; |
-static const char gRoot[] = "root"; |
+static const char gLatnPath[] = "NumberElements/latn"; |
static const UChar u_0 = 0x30; |
static const UChar u_apos = 0x27; |
@@ -94,7 +96,13 @@ class CDFLocaleStyleData : public UMemory { |
// Compute cdfUnits = unitsByVariant[pluralVariant]. |
// Prefix and suffix to use at cdfUnits[log10(x)] |
UHashtable* unitsByVariant; |
- inline CDFLocaleStyleData() : unitsByVariant(NULL) {} |
+ // A flag for whether or not this CDFLocaleStyleData was loaded from the |
+ // Latin numbering system as a fallback from the locale numbering system. |
+ // This value is meaningless if the object is bogus or empty. |
+ UBool fromFallback; |
+ inline CDFLocaleStyleData() : unitsByVariant(NULL), fromFallback(FALSE) { |
+ uprv_memset(divisors, 0, sizeof(divisors)); |
+ } |
~CDFLocaleStyleData(); |
// Init initializes this object. |
void Init(UErrorCode& status); |
@@ -102,6 +110,9 @@ class CDFLocaleStyleData : public UMemory { |
return unitsByVariant == NULL; |
} |
void setToBogus(); |
+ UBool isEmpty() { |
+ return unitsByVariant == NULL || unitsByVariant->count == 0; |
+ } |
private: |
CDFLocaleStyleData(const CDFLocaleStyleData&); |
CDFLocaleStyleData& operator=(const CDFLocaleStyleData&); |
@@ -146,15 +157,12 @@ static const CDFLocaleStyleData* getCDFLocaleStyleData(const Locale& inLocale, U |
static const CDFLocaleStyleData* extractDataByStyleEnum(const CDFLocaleData& data, UNumberCompactStyle style, UErrorCode& status); |
static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& status); |
-static void initCDFLocaleData(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status); |
-static UResourceBundle* tryGetDecimalFallback(const UResourceBundle* numberSystemResource, const char* style, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status); |
-static UResourceBundle* tryGetByKeyWithFallback(const UResourceBundle* rb, const char* path, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status); |
-static UBool isRoot(const UResourceBundle* rb, UErrorCode& status); |
-static void initCDFLocaleStyleData(const UResourceBundle* decimalFormatBundle, CDFLocaleStyleData* result, UErrorCode& status); |
-static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyleData* result, UErrorCode& status); |
-static int32_t populatePrefixSuffix(const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UErrorCode& status); |
+static void load(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status); |
+static int32_t populatePrefixSuffix(const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UBool overwrite, UErrorCode& status); |
+static double calculateDivisor(double power10, int32_t numZeros); |
static UBool onlySpaces(UnicodeString u); |
static void fixQuotes(UnicodeString& s); |
+static void checkForOtherVariants(CDFLocaleStyleData* result, UErrorCode& status); |
static void fillInMissing(CDFLocaleStyleData* result); |
static int32_t computeLog10(double x, UBool inRange); |
static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtable* table, UErrorCode& status); |
@@ -345,7 +353,7 @@ CompactDecimalFormat::format( |
UnicodeString& |
CompactDecimalFormat::format( |
- const StringPiece& /* number */, |
+ StringPiece /* number */, |
UnicodeString& appendTo, |
FieldPositionIterator* /* posIter */, |
UErrorCode& status) const { |
@@ -518,7 +526,8 @@ static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& stat |
return NULL; |
} |
- initCDFLocaleData(inLocale, result, status); |
+ load(inLocale, result, status); |
+ |
if (U_FAILURE(status)) { |
delete result; |
return NULL; |
@@ -526,278 +535,226 @@ static CDFLocaleData* loadCDFLocaleData(const Locale& inLocale, UErrorCode& stat |
return result; |
} |
-// initCDFLocaleData initializes result with data from CLDR. |
-// inLocale is the locale, the CLDR data is stored in result. |
-// We load the UNUM_SHORT and UNUM_LONG data looking first in local numbering |
-// system and not including root locale in fallback. Next we try in the latn |
-// numbering system where we fallback all the way to root. If we don't find |
-// UNUM_SHORT data in these three places, we report an error. If we find |
-// UNUM_SHORT data before finding UNUM_LONG data we make UNUM_LONG data fall |
-// back to UNUM_SHORT data. |
-static void initCDFLocaleData(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status) { |
- LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(inLocale, status)); |
- if (U_FAILURE(status)) { |
- return; |
- } |
- const char* numberingSystemName = ns->getName(); |
- UResourceBundle* rb = ures_open(NULL, inLocale.getName(), &status); |
- rb = ures_getByKeyWithFallback(rb, gNumberElementsTag, rb, &status); |
- if (U_FAILURE(status)) { |
- ures_close(rb); |
- return; |
- } |
- UResourceBundle* shortDataFillIn = NULL; |
- UResourceBundle* longDataFillIn = NULL; |
- UResourceBundle* shortData = NULL; |
- UResourceBundle* longData = NULL; |
+namespace { |
- if (uprv_strcmp(numberingSystemName, gLatnTag) != 0) { |
- LocalUResourceBundlePointer localResource( |
- tryGetByKeyWithFallback(rb, numberingSystemName, NULL, NOT_ROOT, status)); |
- shortData = tryGetDecimalFallback( |
- localResource.getAlias(), gPatternsShort, &shortDataFillIn, NOT_ROOT, status); |
- longData = tryGetDecimalFallback( |
- localResource.getAlias(), gPatternsLong, &longDataFillIn, NOT_ROOT, status); |
- } |
- if (U_FAILURE(status)) { |
- ures_close(shortDataFillIn); |
- ures_close(longDataFillIn); |
- ures_close(rb); |
- return; |
- } |
+struct CmptDecDataSink : public ResourceSink { |
+ |
+ CDFLocaleData& dataBundle; // Where to save values when they are read |
+ UBool isLatin; // Whether or not we are traversing the Latin tree |
+ UBool isFallback; // Whether or not we are traversing the Latin tree as fallback |
+ |
+ enum EPatternsTableKey { PATTERNS_SHORT, PATTERNS_LONG }; |
+ enum EFormatsTableKey { DECIMAL_FORMAT, CURRENCY_FORMAT }; |
- // If we haven't found UNUM_SHORT look in latn numbering system. We must |
- // succeed at finding UNUM_SHORT here. |
- if (shortData == NULL) { |
- LocalUResourceBundlePointer latnResource(tryGetByKeyWithFallback(rb, gLatnTag, NULL, MUST, status)); |
- shortData = tryGetDecimalFallback(latnResource.getAlias(), gPatternsShort, &shortDataFillIn, MUST, status); |
- if (longData == NULL) { |
- longData = tryGetDecimalFallback(latnResource.getAlias(), gPatternsLong, &longDataFillIn, ANY, status); |
- if (longData != NULL && isRoot(longData, status) && !isRoot(shortData, status)) { |
- longData = NULL; |
+ /* |
+ * NumberElements{ <-- top (numbering system table) |
+ * latn{ <-- patternsTable (one per numbering system) |
+ * patternsLong{ <-- formatsTable (one per pattern) |
+ * decimalFormat{ <-- powersOfTenTable (one per format) |
+ * 1000{ <-- pluralVariantsTable (one per power of ten) |
+ * one{"0 thousand"} <-- plural variant and template |
+ */ |
+ |
+ CmptDecDataSink(CDFLocaleData& _dataBundle) |
+ : dataBundle(_dataBundle), isLatin(FALSE), isFallback(FALSE) {} |
+ virtual ~CmptDecDataSink(); |
+ |
+ virtual void put(const char *key, ResourceValue &value, UBool isRoot, UErrorCode &errorCode) { |
+ // SPECIAL CASE: Don't consume root in the non-Latin numbering system |
+ if (isRoot && !isLatin) { return; } |
+ |
+ ResourceTable patternsTable = value.getTable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ for (int i1 = 0; patternsTable.getKeyAndValue(i1, key, value); ++i1) { |
+ |
+ // Check for patternsShort or patternsLong |
+ EPatternsTableKey patternsTableKey; |
+ if (uprv_strcmp(key, gPatternsShort) == 0) { |
+ patternsTableKey = PATTERNS_SHORT; |
+ } else if (uprv_strcmp(key, gPatternsLong) == 0) { |
+ patternsTableKey = PATTERNS_LONG; |
+ } else { |
+ continue; |
} |
- } |
- } |
- initCDFLocaleStyleData(shortData, &result->shortData, status); |
- ures_close(shortDataFillIn); |
- if (U_FAILURE(status)) { |
- ures_close(longDataFillIn); |
- ures_close(rb); |
- } |
- if (longData == NULL) { |
- result->longData.setToBogus(); |
- } else { |
- initCDFLocaleStyleData(longData, &result->longData, status); |
- } |
- ures_close(longDataFillIn); |
- ures_close(rb); |
-} |
- |
-/** |
- * tryGetDecimalFallback attempts to fetch the "decimalFormat" resource bundle |
- * with a particular style. style is either "patternsShort" or "patternsLong." |
- * FillIn, flags, and status work in the same way as in tryGetByKeyWithFallback. |
- */ |
-static UResourceBundle* tryGetDecimalFallback(const UResourceBundle* numberSystemResource, const char* style, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status) { |
- UResourceBundle* first = tryGetByKeyWithFallback(numberSystemResource, style, fillIn, flags, status); |
- UResourceBundle* second = tryGetByKeyWithFallback(first, gDecimalFormatTag, fillIn, flags, status); |
- if (fillIn == NULL) { |
- ures_close(first); |
- } |
- return second; |
-} |
- |
-// tryGetByKeyWithFallback returns a sub-resource bundle that matches given |
-// criteria or NULL if none found. rb is the resource bundle that we are |
-// searching. If rb == NULL then this function behaves as if no sub-resource |
-// is found; path is the key of the sub-resource, |
-// (i.e "foo" but not "foo/bar"); If fillIn is NULL, caller must always call |
-// ures_close() on returned resource. See below for example when fillIn is |
-// not NULL. flags is ANY or NOT_ROOT. Optionally, these values |
-// can be ored with MUST. MUST by itself is the same as ANY | MUST. |
-// The locale of the returned sub-resource will either match the |
-// flags or the returned sub-resouce will be NULL. If MUST is included in |
-// flags, and not suitable sub-resource is found then in addition to returning |
-// NULL, this function also sets status to U_MISSING_RESOURCE_ERROR. If MUST |
-// is not included in flags, then this function just returns NULL if no |
-// such sub-resource is found and will never set status to |
-// U_MISSING_RESOURCE_ERROR. |
-// |
-// Example: This code first searches for "foo/bar" sub-resource without falling |
-// back to ROOT. Then searches for "baz" sub-resource as last resort. |
-// |
-// UResourcebundle* fillIn = NULL; |
-// UResourceBundle* data = tryGetByKeyWithFallback(rb, "foo", &fillIn, NON_ROOT, status); |
-// data = tryGetByKeyWithFallback(data, "bar", &fillIn, NON_ROOT, status); |
-// if (!data) { |
-// data = tryGetbyKeyWithFallback(rb, "baz", &fillIn, MUST, status); |
-// } |
-// if (U_FAILURE(status)) { |
-// ures_close(fillIn); |
-// return; |
-// } |
-// doStuffWithNonNullSubresource(data); |
-// |
-// /* Wrong! don't do the following as it can leak memory if fillIn gets set |
-// to NULL. */ |
-// fillIn = tryGetByKeyWithFallback(rb, "wrong", &fillIn, ANY, status); |
-// |
-// ures_close(fillIn); |
-// |
-static UResourceBundle* tryGetByKeyWithFallback(const UResourceBundle* rb, const char* path, UResourceBundle** fillIn, FallbackFlags flags, UErrorCode& status) { |
- if (U_FAILURE(status)) { |
- return NULL; |
- } |
- UBool must = (flags & MUST); |
- if (rb == NULL) { |
- if (must) { |
- status = U_MISSING_RESOURCE_ERROR; |
- } |
- return NULL; |
- } |
- UResourceBundle* result = NULL; |
- UResourceBundle* ownedByUs = NULL; |
- if (fillIn == NULL) { |
- ownedByUs = ures_getByKeyWithFallback(rb, path, NULL, &status); |
- result = ownedByUs; |
- } else { |
- *fillIn = ures_getByKeyWithFallback(rb, path, *fillIn, &status); |
- result = *fillIn; |
- } |
- if (U_FAILURE(status)) { |
- ures_close(ownedByUs); |
- if (status == U_MISSING_RESOURCE_ERROR && !must) { |
- status = U_ZERO_ERROR; |
- } |
- return NULL; |
- } |
- flags = (FallbackFlags) (flags & ~MUST); |
- switch (flags) { |
- case NOT_ROOT: |
- { |
- UBool bRoot = isRoot(result, status); |
- if (bRoot || U_FAILURE(status)) { |
- ures_close(ownedByUs); |
- if (must && (status == U_ZERO_ERROR)) { |
- status = U_MISSING_RESOURCE_ERROR; |
+ // Traverse into the formats table |
+ ResourceTable formatsTable = value.getTable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ for (int i2 = 0; formatsTable.getKeyAndValue(i2, key, value); ++i2) { |
+ |
+ // Check for decimalFormat or currencyFormat |
+ EFormatsTableKey formatsTableKey; |
+ if (uprv_strcmp(key, gDecimalFormatTag) == 0) { |
+ formatsTableKey = DECIMAL_FORMAT; |
+ // TODO: Enable this statement when currency support is added |
+ // } else if (uprv_strcmp(key, gCurrencyFormat) == 0) { |
+ // formatsTableKey = CURRENCY_FORMAT; |
+ } else { |
+ continue; |
+ } |
+ |
+ // Set the current style and destination based on the two keys |
+ UNumberCompactStyle style; |
+ CDFLocaleStyleData* destination = NULL; |
+ if (patternsTableKey == PATTERNS_LONG |
+ && formatsTableKey == DECIMAL_FORMAT) { |
+ style = UNUM_LONG; |
+ destination = &dataBundle.longData; |
+ } else if (patternsTableKey == PATTERNS_SHORT |
+ && formatsTableKey == DECIMAL_FORMAT) { |
+ style = UNUM_SHORT; |
+ destination = &dataBundle.shortData; |
+ // TODO: Enable the following statements when currency support is added |
+ // } else if (patternsTableKey == PATTERNS_SHORT |
+ // && formatsTableKey == CURRENCY_FORMAT) { |
+ // style = UNUM_SHORT_CURRENCY; // or whatever the enum gets named |
+ // destination = &dataBundle.shortCurrencyData; |
+ // } else { |
+ // // Silently ignore this case |
+ // continue; |
+ } |
+ |
+ // SPECIAL CASE: RULES FOR WHETHER OR NOT TO CONSUME THIS TABLE: |
+ // 1) Don't consume longData if shortData was consumed from the non-Latin |
+ // locale numbering system |
+ // 2) Don't consume longData for the first time if this is the root bundle and |
+ // shortData is already populated from a more specific locale. Note that if |
+ // both longData and shortData are both only in root, longData will be |
+ // consumed since it is alphabetically before shortData in the bundle. |
+ if (isFallback |
+ && style == UNUM_LONG |
+ && !dataBundle.shortData.isEmpty() |
+ && !dataBundle.shortData.fromFallback) { |
+ continue; |
+ } |
+ if (isRoot |
+ && style == UNUM_LONG |
+ && dataBundle.longData.isEmpty() |
+ && !dataBundle.shortData.isEmpty()) { |
+ continue; |
+ } |
+ |
+ // Set the "fromFallback" flag on the data object |
+ destination->fromFallback = isFallback; |
+ |
+ // Traverse into the powers of ten table |
+ ResourceTable powersOfTenTable = value.getTable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) { |
+ |
+ // The key will always be some even power of 10. e.g 10000. |
+ char* endPtr = NULL; |
+ double power10 = uprv_strtod(key, &endPtr); |
+ if (*endPtr != 0) { |
+ errorCode = U_INTERNAL_PROGRAM_ERROR; |
+ return; |
+ } |
+ int32_t log10Value = computeLog10(power10, FALSE); |
+ |
+ // Silently ignore divisors that are too big. |
+ if (log10Value >= MAX_DIGITS) continue; |
+ |
+ // Iterate over the plural variants ("one", "other", etc) |
+ ResourceTable pluralVariantsTable = value.getTable(errorCode); |
+ if (U_FAILURE(errorCode)) { return; } |
+ for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) { |
+ const char* pluralVariant = key; |
+ const UnicodeString formatStr = value.getUnicodeString(errorCode); |
+ |
+ // Copy the data into the in-memory data bundle (do not overwrite |
+ // existing values) |
+ int32_t numZeros = populatePrefixSuffix( |
+ pluralVariant, log10Value, formatStr, |
+ destination->unitsByVariant, FALSE, errorCode); |
+ |
+ // If populatePrefixSuffix returns -1, it means that this key has been |
+ // encountered already. |
+ if (numZeros < 0) { |
+ continue; |
+ } |
+ |
+ // Set the divisor, which is based on the number of zeros in the template |
+ // string. If the divisor from here is different from the one previously |
+ // stored, it means that the number of zeros in different plural variants |
+ // differs; throw an exception. |
+ // TODO: How should I check for floating-point errors here? |
+ // Is there a good reason why "divisor" is double and not long like Java? |
+ double divisor = calculateDivisor(power10, numZeros); |
+ if (destination->divisors[log10Value] != 0.0 |
+ && destination->divisors[log10Value] != divisor) { |
+ errorCode = U_INTERNAL_PROGRAM_ERROR; |
+ return; |
+ } |
+ destination->divisors[log10Value] = divisor; |
} |
- return NULL; |
} |
- return result; |
} |
- case ANY: |
- return result; |
- default: |
- ures_close(ownedByUs); |
- status = U_ILLEGAL_ARGUMENT_ERROR; |
- return NULL; |
+ } |
} |
-} |
+}; |
-static UBool isRoot(const UResourceBundle* rb, UErrorCode& status) { |
- const char* actualLocale = ures_getLocaleByType( |
- rb, ULOC_ACTUAL_LOCALE, &status); |
- if (U_FAILURE(status)) { |
- return FALSE; |
- } |
- return uprv_strcmp(actualLocale, gRoot) == 0; |
-} |
+// Virtual destructors must be defined out of line. |
+CmptDecDataSink::~CmptDecDataSink() {} |
+} // namespace |
-// initCDFLocaleStyleData loads formatting data for a particular style. |
-// decimalFormatBundle is the "decimalFormat" resource bundle in CLDR. |
-// Loaded data stored in result. |
-static void initCDFLocaleStyleData(const UResourceBundle* decimalFormatBundle, CDFLocaleStyleData* result, UErrorCode& status) { |
+static void load(const Locale& inLocale, CDFLocaleData* result, UErrorCode& status) { |
+ LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(inLocale, status)); |
if (U_FAILURE(status)) { |
return; |
} |
- // Iterate through all the powers of 10. |
- int32_t size = ures_getSize(decimalFormatBundle); |
- UResourceBundle* power10 = NULL; |
- for (int32_t i = 0; i < size; ++i) { |
- power10 = ures_getByIndex(decimalFormatBundle, i, power10, &status); |
- if (U_FAILURE(status)) { |
- ures_close(power10); |
- return; |
- } |
- populatePower10(power10, result, status); |
- if (U_FAILURE(status)) { |
- ures_close(power10); |
- return; |
- } |
- } |
- ures_close(power10); |
- fillInMissing(result); |
-} |
+ const char* nsName = ns->getName(); |
-// populatePower10 grabs data for a particular power of 10 from CLDR. |
-// The loaded data is stored in result. |
-static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyleData* result, UErrorCode& status) { |
+ LocalUResourceBundlePointer resource(ures_open(NULL, inLocale.getName(), &status)); |
if (U_FAILURE(status)) { |
return; |
} |
- char* endPtr = NULL; |
- double power10 = uprv_strtod(ures_getKey(power10Bundle), &endPtr); |
- if (*endPtr != 0) { |
- status = U_INTERNAL_PROGRAM_ERROR; |
- return; |
- } |
- int32_t log10Value = computeLog10(power10, FALSE); |
- // Silently ignore divisors that are too big. |
- if (log10Value == MAX_DIGITS) { |
- return; |
- } |
- int32_t size = ures_getSize(power10Bundle); |
- int32_t numZeros = 0; |
- UBool otherVariantDefined = FALSE; |
- UResourceBundle* variantBundle = NULL; |
- // Iterate over all the plural variants for the power of 10 |
- for (int32_t i = 0; i < size; ++i) { |
- variantBundle = ures_getByIndex(power10Bundle, i, variantBundle, &status); |
- if (U_FAILURE(status)) { |
- ures_close(variantBundle); |
- return; |
- } |
- const char* variant = ures_getKey(variantBundle); |
- int32_t resLen; |
- const UChar* formatStrP = ures_getString(variantBundle, &resLen, &status); |
- if (U_FAILURE(status)) { |
- ures_close(variantBundle); |
- return; |
- } |
- UnicodeString formatStr(false, formatStrP, resLen); |
- if (uprv_strcmp(variant, gOther) == 0) { |
- otherVariantDefined = TRUE; |
- } |
- int32_t nz = populatePrefixSuffix( |
- variant, log10Value, formatStr, result->unitsByVariant, status); |
- if (U_FAILURE(status)) { |
- ures_close(variantBundle); |
+ CmptDecDataSink sink(*result); |
+ sink.isFallback = FALSE; |
+ |
+ // First load the number elements data if nsName is not Latin. |
+ if (uprv_strcmp(nsName, gLatnTag) != 0) { |
+ sink.isLatin = FALSE; |
+ CharString path; |
+ path.append(gNumberElementsTag, status) |
+ .append('/', status) |
+ .append(nsName, status); |
+ ures_getAllItemsWithFallback(resource.getAlias(), path.data(), sink, status); |
+ if (status == U_MISSING_RESOURCE_ERROR) { |
+ // Silently ignore and use Latin |
+ status = U_ZERO_ERROR; |
+ } else if (U_FAILURE(status)) { |
return; |
} |
- if (nz != numZeros) { |
- // We expect all format strings to have the same number of 0's |
- // left of the decimal point. |
- if (numZeros != 0) { |
- status = U_INTERNAL_PROGRAM_ERROR; |
- ures_close(variantBundle); |
- return; |
- } |
- numZeros = nz; |
- } |
+ sink.isFallback = TRUE; |
} |
- ures_close(variantBundle); |
- // We expect to find an OTHER variant for each power of 10. |
- if (!otherVariantDefined) { |
- status = U_INTERNAL_PROGRAM_ERROR; |
- return; |
+ |
+ // Now load Latin. |
+ sink.isLatin = TRUE; |
+ ures_getAllItemsWithFallback(resource.getAlias(), gLatnPath, sink, status); |
+ if (U_FAILURE(status)) return; |
+ |
+ // If longData is empty, default it to be equal to shortData |
+ if (result->longData.isEmpty()) { |
+ result->longData.setToBogus(); |
} |
- double divisor = power10; |
- for (int32_t i = 1; i < numZeros; ++i) { |
- divisor /= 10.0; |
+ |
+ // Check for "other" variants in each of the three data classes, and resolve missing elements. |
+ |
+ if (!result->longData.isBogus()) { |
+ checkForOtherVariants(&result->longData, status); |
+ if (U_FAILURE(status)) return; |
+ fillInMissing(&result->longData); |
} |
- result->divisors[log10Value] = divisor; |
+ |
+ checkForOtherVariants(&result->shortData, status); |
+ if (U_FAILURE(status)) return; |
+ fillInMissing(&result->shortData); |
+ |
+ // TODO: Enable this statement when currency support is added |
+ // checkForOtherVariants(&result->shortCurrencyData, status); |
+ // if (U_FAILURE(status)) return; |
+ // fillInMissing(&result->shortCurrencyData); |
} |
// populatePrefixSuffix Adds a specific prefix-suffix pair to result for a |
@@ -810,7 +767,7 @@ static void populatePower10(const UResourceBundle* power10Bundle, CDFLocaleStyle |
// In the special case that formatStr contains only spaces for prefix |
// and suffix, populatePrefixSuffix returns log10Value + 1. |
static int32_t populatePrefixSuffix( |
- const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UErrorCode& status) { |
+ const char* variant, int32_t log10Value, const UnicodeString& formatStr, UHashtable* result, UBool overwrite, UErrorCode& status) { |
if (U_FAILURE(status)) { |
return 0; |
} |
@@ -825,6 +782,13 @@ static int32_t populatePrefixSuffix( |
if (U_FAILURE(status)) { |
return 0; |
} |
+ |
+ // Return -1 if we are not overwriting an existing value |
+ if (unit->isSet() && !overwrite) { |
+ return -1; |
+ } |
+ unit->markAsSet(); |
+ |
// Everything up to first 0 is the prefix |
unit->prefix = formatStr.tempSubString(0, firstIdx); |
fixQuotes(unit->prefix); |
@@ -846,6 +810,16 @@ static int32_t populatePrefixSuffix( |
return (idx - firstIdx); |
} |
+// Calculate a divisor based on the magnitude and number of zeros in the |
+// template string. |
+static double calculateDivisor(double power10, int32_t numZeros) { |
+ double divisor = power10; |
+ for (int32_t i = 1; i < numZeros; ++i) { |
+ divisor /= 10.0; |
+ } |
+ return divisor; |
+} |
+ |
static UBool onlySpaces(UnicodeString u) { |
return u.trim().length() == 0; |
} |
@@ -884,6 +858,38 @@ static void fixQuotes(UnicodeString& s) { |
s.truncate(dest); |
} |
+// Checks to make sure that an "other" variant is present in all |
+// powers of 10. |
+static void checkForOtherVariants(CDFLocaleStyleData* result, |
+ UErrorCode& status) { |
+ if (result == NULL || result->unitsByVariant == NULL) { |
+ return; |
+ } |
+ |
+ const CDFUnit* otherByBase = |
+ (const CDFUnit*) uhash_get(result->unitsByVariant, gOther); |
+ if (otherByBase == NULL) { |
+ status = U_INTERNAL_PROGRAM_ERROR; |
+ return; |
+ } |
+ |
+ // Check all other plural variants, and make sure that if |
+ // any of them are populated, then other is also populated |
+ int32_t pos = UHASH_FIRST; |
+ const UHashElement* element; |
+ while ((element = uhash_nextElement(result->unitsByVariant, &pos)) != NULL) { |
+ CDFUnit* variantsByBase = (CDFUnit*) element->value.pointer; |
+ if (variantsByBase == otherByBase) continue; |
+ for (int32_t log10Value = 0; log10Value < MAX_DIGITS; ++log10Value) { |
+ if (variantsByBase[log10Value].isSet() |
+ && !otherByBase[log10Value].isSet()) { |
+ status = U_INTERNAL_PROGRAM_ERROR; |
+ return; |
+ } |
+ } |
+ } |
+} |
+ |
// fillInMissing ensures that the data in result is complete. |
// result data is complete if for each variant in result, there exists |
// a prefix-suffix pair for each log10 value and there also exists |
@@ -973,7 +979,6 @@ static CDFUnit* createCDFUnit(const char* variant, int32_t log10Value, UHashtabl |
} |
} |
CDFUnit* result = &cdfUnit[log10Value]; |
- result->markAsSet(); |
return result; |
} |