1ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert// © 2017 and later: Unicode, Inc. and others. 2ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html 3ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 4ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "unicode/utypes.h" 5ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 6ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT 7ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 8ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "resource.h" 9ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "number_compact.h" 10ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "unicode/ustring.h" 11ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "unicode/ures.h" 12ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "cstring.h" 13ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "charstr.h" 14ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#include "uresimp.h" 15ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 16ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertusing namespace icu; 17ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertusing namespace icu::number; 18ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertusing namespace icu::number::impl; 19ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 20ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertnamespace { 21ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 22ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert// A dummy object used when a "0" compact decimal entry is encountered. This is necessary 23ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert// in order to prevent falling back to root. Object equality ("==") is intended. 24ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertconst UChar *USE_FALLBACK = u"<USE FALLBACK>"; 25ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 26ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert/** Produces a string like "NumberElements/latn/patternsShort/decimalFormat". */ 27ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid getResourceBundleKey(const char *nsName, CompactStyle compactStyle, CompactType compactType, 28ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert CharString &sb, UErrorCode &status) { 29ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert sb.clear(); 30ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert sb.append("NumberElements/", status); 31ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert sb.append(nsName, status); 32ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert sb.append(compactStyle == CompactStyle::UNUM_SHORT ? "/patternsShort" : "/patternsLong", status); 33ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert sb.append(compactType == CompactType::TYPE_DECIMAL ? "/decimalFormat" : "/currencyFormat", status); 34ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 35ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 36ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertint32_t getIndex(int32_t magnitude, StandardPlural::Form plural) { 37ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert return magnitude * StandardPlural::COUNT + plural; 38ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 39ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 40ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertint32_t countZeros(const UChar *patternString, int32_t patternLength) { 41ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // NOTE: This strategy for computing the number of zeros is a hack for efficiency. 42ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // It could break if there are any 0s that aren't part of the main pattern. 43ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert int32_t numZeros = 0; 44ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert for (int32_t i = 0; i < patternLength; i++) { 45ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (patternString[i] == u'0') { 46ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert numZeros++; 47ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } else if (numZeros > 0) { 48ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert break; // zeros should always be contiguous 49ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 50ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 51ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert return numZeros; 52ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 53ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 54ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} // namespace 55ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 56ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert// NOTE: patterns and multipliers both get zero-initialized. 57ffdc27edd5503111189fc11165c5a11289a71f79Fredrik RoubertCompactData::CompactData() : patterns(), multipliers(), largestMagnitude(0), isEmpty(TRUE) { 58ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 59ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 60ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid CompactData::populate(const Locale &locale, const char *nsName, CompactStyle compactStyle, 61ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert CompactType compactType, UErrorCode &status) { 62ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert CompactDataSink sink(*this); 63ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert LocalUResourceBundlePointer rb(ures_open(nullptr, locale.getName(), &status)); 64ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 65ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 66ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert bool nsIsLatn = strcmp(nsName, "latn") == 0; 67ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert bool compactIsShort = compactStyle == CompactStyle::UNUM_SHORT; 68ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 69ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Fall back to latn numbering system and/or short compact style. 70ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert CharString resourceKey; 71ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert getResourceBundleKey(nsName, compactStyle, compactType, resourceKey, status); 72ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert UErrorCode localStatus = U_ZERO_ERROR; 73ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); 74ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (isEmpty && !nsIsLatn) { 75ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert getResourceBundleKey("latn", compactStyle, compactType, resourceKey, status); 76ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert localStatus = U_ZERO_ERROR; 77ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); 78ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 79ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (isEmpty && !compactIsShort) { 80ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert getResourceBundleKey(nsName, CompactStyle::UNUM_SHORT, compactType, resourceKey, status); 81ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert localStatus = U_ZERO_ERROR; 82ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); 83ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 84ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (isEmpty && !nsIsLatn && !compactIsShort) { 85ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert getResourceBundleKey("latn", CompactStyle::UNUM_SHORT, compactType, resourceKey, status); 86ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert localStatus = U_ZERO_ERROR; 87ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert ures_getAllItemsWithFallback(rb.getAlias(), resourceKey.data(), sink, localStatus); 88ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 89ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 90ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // The last fallback should be guaranteed to return data. 91ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (isEmpty) { 92ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert status = U_INTERNAL_PROGRAM_ERROR; 93ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 94ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 95ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 96ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertint32_t CompactData::getMultiplier(int32_t magnitude) const { 97ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (magnitude < 0) { 98ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert return 0; 99ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 100ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (magnitude > largestMagnitude) { 101ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert magnitude = largestMagnitude; 102ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 103ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert return multipliers[magnitude]; 104ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 105ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 106ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertconst UChar *CompactData::getPattern(int32_t magnitude, StandardPlural::Form plural) const { 107ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (magnitude < 0) { 108ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert return nullptr; 109ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 110ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (magnitude > largestMagnitude) { 111ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert magnitude = largestMagnitude; 112ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 113ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert const UChar *patternString = patterns[getIndex(magnitude, plural)]; 114ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (patternString == nullptr && plural != StandardPlural::OTHER) { 115ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Fall back to "other" plural variant 116ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert patternString = patterns[getIndex(magnitude, StandardPlural::OTHER)]; 117ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 118ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (patternString == USE_FALLBACK) { // == is intended 119ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Return null if USE_FALLBACK is present 120ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert patternString = nullptr; 121ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 122ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert return patternString; 123ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 124ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 125ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid CompactData::getUniquePatterns(UVector &output, UErrorCode &status) const { 126ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert U_ASSERT(output.isEmpty()); 127ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // NOTE: In C++, this is done more manually with a UVector. 128ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // In Java, we can take advantage of JDK HashSet. 129ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert for (auto pattern : patterns) { 130ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (pattern == nullptr || pattern == USE_FALLBACK) { 131ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert continue; 132ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 133ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 134ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Insert pattern into the UVector if the UVector does not already contain the pattern. 135ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Search the UVector from the end since identical patterns are likely to be adjacent. 136ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert for (int32_t i = output.size() - 1; i >= 0; i--) { 137ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (u_strcmp(pattern, static_cast<const UChar *>(output[i])) == 0) { 138ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert goto continue_outer; 139ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 140ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 141ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 142ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // The string was not found; add it to the UVector. 143ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // ANDY: This requires a const_cast. Why? 144ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert output.addElement(const_cast<UChar *>(pattern), status); 145ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 146ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert continue_outer: 147ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert continue; 148ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 149ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 150ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 151ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid CompactData::CompactDataSink::put(const char *key, ResourceValue &value, UBool /*noFallback*/, 152ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert UErrorCode &status) { 153ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // traverse into the table of powers of ten 154ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert ResourceTable powersOfTenTable = value.getTable(status); 155ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 156ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) { 157ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 158ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Assumes that the keys are always of the form "10000" where the magnitude is the 159ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // length of the key minus one. We expect magnitudes to be less than MAX_DIGITS. 160ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert auto magnitude = static_cast<int8_t> (strlen(key) - 1); 161ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert int8_t multiplier = data.multipliers[magnitude]; 162ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert U_ASSERT(magnitude < COMPACT_MAX_DIGITS); 163ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 164ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Iterate over the plural variants ("one", "other", etc) 165ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert ResourceTable pluralVariantsTable = value.getTable(status); 166ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 167ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) { 168ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 169ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Skip this magnitude/plural if we already have it from a child locale. 170ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Note: This also skips USE_FALLBACK entries. 171ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert StandardPlural::Form plural = StandardPlural::fromString(key, status); 172ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 173ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (data.patterns[getIndex(magnitude, plural)] != nullptr) { 174ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert continue; 175ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 176ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 177ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // The value "0" means that we need to use the default pattern and not fall back 178ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // to parent locales. Example locale where this is relevant: 'it'. 179ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert int32_t patternLength; 180ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert const UChar *patternString = value.getString(patternLength, status); 181ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 182ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (u_strcmp(patternString, u"0") == 0) { 183ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert patternString = USE_FALLBACK; 184ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert patternLength = 0; 185ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 186ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 187ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Save the pattern string. We will parse it lazily. 188ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert data.patterns[getIndex(magnitude, plural)] = patternString; 189ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 190ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // If necessary, compute the multiplier: the difference between the magnitude 191ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // and the number of zeros in the pattern. 192ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (multiplier == 0) { 193ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert int32_t numZeros = countZeros(patternString, patternLength); 194ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun" 195ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert multiplier = static_cast<int8_t> (numZeros - magnitude - 1); 196ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 197ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 198ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 199ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 200ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Save the multiplier. 201ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (data.multipliers[magnitude] == 0) { 202ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert data.multipliers[magnitude] = multiplier; 203ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (magnitude > data.largestMagnitude) { 204ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert data.largestMagnitude = magnitude; 205ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 206ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert data.isEmpty = false; 207ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } else { 208ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert U_ASSERT(data.multipliers[magnitude] == multiplier); 209ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 210ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 211ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 212ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 213ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert/////////////////////////////////////////////////////////// 214ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert/// END OF CompactData.java; BEGIN CompactNotation.java /// 215ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert/////////////////////////////////////////////////////////// 216ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 217ffdc27edd5503111189fc11165c5a11289a71f79Fredrik RoubertCompactHandler::CompactHandler(CompactStyle compactStyle, const Locale &locale, const char *nsName, 218ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert CompactType compactType, const PluralRules *rules, 219ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert MutablePatternModifier *buildReference, const MicroPropsGenerator *parent, 220ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert UErrorCode &status) 221ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert : rules(rules), parent(parent) { 222ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert data.populate(locale, nsName, compactStyle, compactType, status); 223ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (buildReference != nullptr) { 224ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Safe code path 225ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert precomputeAllModifiers(*buildReference, status); 226ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert safe = TRUE; 227ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } else { 228ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Unsafe code path 229ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert safe = FALSE; 230ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 231ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 232ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 233ffdc27edd5503111189fc11165c5a11289a71f79Fredrik RoubertCompactHandler::~CompactHandler() { 234ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert for (int32_t i = 0; i < precomputedModsLength; i++) { 235ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert delete precomputedMods[i].mod; 236ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 237ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 238ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 239ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid CompactHandler::precomputeAllModifiers(MutablePatternModifier &buildReference, UErrorCode &status) { 240ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 241ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 242ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Initial capacity of 12 for 0K, 00K, 000K, ...M, ...B, and ...T 243ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert UVector allPatterns(12, status); 244ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 245ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert data.getUniquePatterns(allPatterns, status); 246ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 247ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 248ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // C++ only: ensure that precomputedMods has room. 249ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert precomputedModsLength = allPatterns.size(); 250ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (precomputedMods.getCapacity() < precomputedModsLength) { 251ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert precomputedMods.resize(allPatterns.size(), status); 252ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 253ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 254ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 255ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert for (int32_t i = 0; i < precomputedModsLength; i++) { 256ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert auto patternString = static_cast<const UChar *>(allPatterns[i]); 257ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert UnicodeString hello(patternString); 258ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert CompactModInfo &info = precomputedMods[i]; 259ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert ParsedPatternInfo patternInfo; 260ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert PatternParser::parseToPatternInfo(UnicodeString(patternString), patternInfo, status); 261ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 262ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert buildReference.setPatternInfo(&patternInfo); 263ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert info.mod = buildReference.createImmutable(status); 264ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 265ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert info.numDigits = patternInfo.positive.integerTotal; 266ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert info.patternString = patternString; 267ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 268ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 269ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 270ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubertvoid CompactHandler::processQuantity(DecimalQuantity &quantity, MicroProps µs, 271ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert UErrorCode &status) const { 272ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert parent->processQuantity(quantity, micros, status); 273ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (U_FAILURE(status)) { return; } 274ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 275ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Treat zero as if it had magnitude 0 276ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert int magnitude; 277ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (quantity.isZero()) { 278ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert magnitude = 0; 279ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert micros.rounding.apply(quantity, status); 280ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } else { 281ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // TODO: Revisit chooseMultiplierAndApply 282ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert int multiplier = micros.rounding.chooseMultiplierAndApply(quantity, data, status); 283ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert magnitude = quantity.isZero() ? 0 : quantity.getMagnitude(); 284ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert magnitude -= multiplier; 285ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 286ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 287ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert StandardPlural::Form plural = quantity.getStandardPlural(rules); 288ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert const UChar *patternString = data.getPattern(magnitude, plural); 289ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert int numDigits = -1; 290ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (patternString == nullptr) { 291ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Use the default (non-compact) modifier. 292ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // No need to take any action. 293ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } else if (safe) { 294ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Safe code path. 295ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Java uses a hash set here for O(1) lookup. C++ uses a linear search. 296ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // TODO: Benchmark this and maybe change to a binary search or hash table. 297ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert int32_t i = 0; 298ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert for (; i < precomputedModsLength; i++) { 299ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert const CompactModInfo &info = precomputedMods[i]; 300ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert if (u_strcmp(patternString, info.patternString) == 0) { 301ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert info.mod->applyToMicros(micros, quantity); 302ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert numDigits = info.numDigits; 303ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert break; 304ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 305ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 306ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // It should be guaranteed that we found the entry. 307ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert U_ASSERT(i < precomputedModsLength); 308ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } else { 309ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Unsafe code path. 310ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // Overwrite the PatternInfo in the existing modMiddle. 311ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // C++ Note: Use unsafePatternInfo for proper lifecycle. 312ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert ParsedPatternInfo &patternInfo = const_cast<CompactHandler *>(this)->unsafePatternInfo; 313ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert PatternParser::parseToPatternInfo(UnicodeString(patternString), patternInfo, status); 314ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert static_cast<MutablePatternModifier*>(const_cast<Modifier*>(micros.modMiddle)) 315ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert ->setPatternInfo(&patternInfo); 316ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert numDigits = patternInfo.positive.integerTotal; 317ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert } 318ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 319ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // FIXME: Deal with numDigits == 0 (Awaiting a test case) 320ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert (void)numDigits; 321ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 322ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert // We already performed rounding. Do not perform it again. 323ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert micros.rounding = Rounder::constructPassThrough(); 324ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert} 325ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert 326ffdc27edd5503111189fc11165c5a11289a71f79Fredrik Roubert#endif /* #if !UCONFIG_NO_FORMATTING */ 327