• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "locale_config.h"
16 #include "ohos/init_data.h"
17 #include "utils.h"
18 #include "number_format.h"
19 
20 namespace OHOS {
21 namespace Global {
22 namespace I18n {
23 bool NumberFormat::icuInitialized = NumberFormat::Init();
24 
25 std::unordered_map<std::string, UNumberUnitWidth> NumberFormat::unitStyle = {
26     { "long", UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME },
27     { "short", UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT },
28     { "narrow", UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW }
29 };
30 
31 std::unordered_map<std::string, UNumberUnitWidth> NumberFormat::currencyStyle = {
32     { "symbol", UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT },
33     { "code", UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE },
34     { "name", UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME },
35     { "narrowSymbol", UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW }
36 };
37 
38 std::unordered_map<std::string, UNumberSignDisplay> NumberFormat::signAutoStyle = {
39     { "auto", UNumberSignDisplay::UNUM_SIGN_AUTO },
40     { "never", UNumberSignDisplay::UNUM_SIGN_NEVER },
41     { "always", UNumberSignDisplay::UNUM_SIGN_ALWAYS },
42     { "exceptZero", UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO }
43 };
44 
45 std::unordered_map<std::string, UNumberSignDisplay> NumberFormat::signAccountingStyle = {
46     { "auto", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING },
47     { "never", UNumberSignDisplay::UNUM_SIGN_NEVER },
48     { "always", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS },
49     { "exceptZero", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO }
50 };
51 
52 std::unordered_map<UMeasurementSystem, std::string> NumberFormat::measurementSystem = {
53     { UMeasurementSystem::UMS_SI, "SI" },
54     { UMeasurementSystem::UMS_US, "US" },
55     { UMeasurementSystem::UMS_UK, "UK" },
56 };
57 
NumberFormat(const std::vector<std::string> & localeTags,std::map<std::string,std::string> & configs)58 NumberFormat::NumberFormat(const std::vector<std::string> &localeTags, std::map<std::string, std::string> &configs)
59 {
60     UErrorCode status = U_ZERO_ERROR;
61     std::unique_ptr<icu::LocaleBuilder> builder = nullptr;
62     builder = std::make_unique<icu::LocaleBuilder>();
63     ParseConfigs(configs);
64     for (size_t i = 0; i < localeTags.size(); i++) {
65         std::string curLocale = localeTags[i];
66         locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status);
67         if (status != U_ZERO_ERROR) {
68             status = U_ZERO_ERROR;
69             continue;
70         }
71         if (LocaleInfo::allValidLocales.count(locale.getLanguage()) > 0) {
72             localeInfo = std::make_unique<LocaleInfo>(curLocale, configs);
73             if (!localeInfo->InitSuccess()) {
74                 continue;
75             }
76             locale = localeInfo->GetLocale();
77             localeBaseName = localeInfo->GetBaseName();
78             numberFormat = icu::number::NumberFormatter::withLocale(locale);
79             icu::MeasureUnit::getAvailable(unitArray, MAX_UNIT_NUM, status);
80             if (!U_SUCCESS(status)) {
81                 status = U_ZERO_ERROR;
82                 continue;
83             }
84             createSuccess = true;
85             break;
86         }
87     }
88     if (!createSuccess) {
89         localeInfo = std::make_unique<LocaleInfo>(LocaleConfig::GetSystemLocale(), configs);
90         if (localeInfo->InitSuccess()) {
91             locale = localeInfo->GetLocale();
92             localeBaseName = localeInfo->GetBaseName();
93             numberFormat = icu::number::NumberFormatter::withLocale(locale);
94             icu::MeasureUnit::getAvailable(unitArray, MAX_UNIT_NUM, status);
95             if (!U_SUCCESS(status)) {
96                 createSuccess = true;
97             }
98         }
99     }
100     if (createSuccess) {
101         InitProperties();
102     }
103 }
104 
~NumberFormat()105 NumberFormat::~NumberFormat()
106 {
107 }
108 
InitProperties()109 void NumberFormat::InitProperties()
110 {
111     if (!currency.empty()) {
112         UErrorCode status = U_ZERO_ERROR;
113         numberFormat =
114             numberFormat.unit(icu::CurrencyUnit(icu::UnicodeString(currency.c_str()).getBuffer(), status));
115         if (currencyDisplay != UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT) {
116             numberFormat = numberFormat.unitWidth(currencyDisplay);
117         }
118     }
119     if (!styleString.empty() && styleString == "percent") {
120         numberFormat = numberFormat.unit(icu::NoUnit::percent())
121             .scale(icu::number::Scale::powerOfTen(POWER_VALUE))
122             .precision(icu::number::Precision::fixedFraction(0));
123     }
124     if (!styleString.empty() && styleString == "unit") {
125         for (icu::MeasureUnit curUnit : unitArray) {
126             if (!strcmp(curUnit.getSubtype(), unit.c_str())) {
127                 numberFormat = numberFormat.unit(curUnit);
128                 unitType = curUnit.getType();
129             }
130         }
131         UErrorCode status = U_ZERO_ERROR;
132         UMeasurementSystem measSys = ulocdata_getMeasurementSystem(localeBaseName.c_str(), &status);
133         if (U_SUCCESS(status) && measSys >= 0) {
134             unitMeasSys = measurementSystem[measSys];
135         }
136         numberFormat = numberFormat.unitWidth(unitDisplay);
137         numberFormat = numberFormat.precision(icu::number::Precision::maxFraction(DEFAULT_FRACTION_DIGITS));
138     }
139     if (!useGrouping.empty()) {
140         numberFormat = numberFormat.grouping((useGrouping == "true") ?
141             UNumberGroupingStrategy::UNUM_GROUPING_AUTO : UNumberGroupingStrategy::UNUM_GROUPING_OFF);
142     }
143     if (!currencySign.empty() || !signDisplayString.empty()) {
144         numberFormat = numberFormat.sign(signDisplay);
145     }
146     if (!notationString.empty()) {
147         numberFormat = numberFormat.notation(notation);
148     }
149     InitDigitsProperties();
150 }
151 
InitDigitsProperties()152 void NumberFormat::InitDigitsProperties()
153 {
154     int32_t status = 0;
155     if (!maximumSignificantDigits.empty() || !minimumSignificantDigits.empty()) {
156         int32_t maxSignificantDigits = ConvertString2Int(maximumSignificantDigits, status);
157         if (status == 0) {
158             numberFormat = numberFormat.precision(icu::number::Precision::maxSignificantDigits(maxSignificantDigits));
159         }
160 
161         status = 0;
162         int32_t minSignificantDigits = ConvertString2Int(minimumSignificantDigits, status);
163         if (status == 0) {
164             numberFormat = numberFormat.precision(icu::number::Precision::minSignificantDigits(minSignificantDigits));
165         }
166     } else {
167         int32_t minIntegerDigits = ConvertString2Int(minimumIntegerDigits, status);
168         if (status == 0 && minIntegerDigits > 1) {
169             numberFormat =
170                 numberFormat.integerWidth(icu::number::IntegerWidth::zeroFillTo(minIntegerDigits));
171         }
172 
173         status = 0;
174         int32_t minFractionDigits = ConvertString2Int(minimumFractionDigits, status);
175         if (status == 0) {
176             numberFormat =
177                 numberFormat.precision(icu::number::Precision::minFraction(minFractionDigits));
178         }
179 
180         status = 0;
181         int32_t maxFractionDigits = ConvertString2Int(maximumFractionDigits, status);
182         if (status == 0) {
183             numberFormat =
184                 numberFormat.precision(icu::number::Precision::maxFraction(maxFractionDigits));
185         }
186     }
187 }
188 
ParseConfigs(std::map<std::string,std::string> & configs)189 void NumberFormat::ParseConfigs(std::map<std::string, std::string> &configs)
190 {
191     if (configs.count("signDisplay") > 0) {
192         signDisplayString = configs["signDisplay"];
193     }
194     if (signAutoStyle.count(signDisplayString) > 0) {
195         signDisplay = signAutoStyle[signDisplayString];
196     }
197     if (configs.count("style") > 0) {
198         styleString = configs["style"];
199     }
200     if (styleString == "unit" && configs.count("unit") > 0) {
201         unit = configs["unit"];
202         if (configs.count("unitDisplay") > 0) {
203             unitDisplayString = configs["unitDisplay"];
204             if (unitStyle.count(unitDisplayString) > 0) {
205                 unitDisplay = unitStyle[unitDisplayString];
206             }
207         }
208         if (configs.count("unitUsage") > 0) {
209             unitUsage = configs["unitUsage"];
210         }
211     }
212     if (styleString == "currency" && configs.count("currency") > 0) {
213         currency = configs["currency"];
214         if (configs.count("currencySign") > 0) {
215             currencySign = configs["currencySign"];
216         }
217         if (currencySign.compare("accounting") == 0 && signAccountingStyle.count(signDisplayString) > 0) {
218             signDisplay = signAccountingStyle[signDisplayString];
219         }
220         if (configs.count("currencyDisplay") > 0 && currencyStyle.count(configs["currencyDisplay"]) > 0) {
221             currencyDisplayString = configs["currencyDisplay"];
222             currencyDisplay = currencyStyle[currencyDisplayString];
223         }
224     }
225     ParseDigitsConfigs(configs);
226 }
227 
ParseDigitsConfigs(std::map<std::string,std::string> & configs)228 void NumberFormat::ParseDigitsConfigs(std::map<std::string, std::string> &configs)
229 {
230     if (configs.count("notation") > 0) {
231         notationString = configs["notation"];
232         if (notationString == "scientific") {
233             notation = icu::number::Notation::scientific();
234         } else if (notationString == "engineering") {
235             notation = icu::number::Notation::engineering();
236         }
237         if (notationString == "compact" && configs.count("compactDisplay") > 0) {
238             compactDisplay = configs["compactDisplay"];
239             if (compactDisplay == "long") {
240                 notation = icu::number::Notation::compactLong();
241             } else {
242                 notation = icu::number::Notation::compactShort();
243             }
244         }
245     }
246     if (configs.count("minimumIntegerDigits") > 0) {
247         minimumIntegerDigits = configs["minimumIntegerDigits"];
248     }
249     if (configs.count("minimumFractionDigits") > 0) {
250         minimumFractionDigits = configs["minimumFractionDigits"];
251     }
252     if (configs.count("maximumFractionDigits") > 0) {
253         maximumFractionDigits = configs["maximumFractionDigits"];
254     }
255     if (configs.count("minimumSignificantDigits") > 0) {
256         minimumSignificantDigits = configs["minimumSignificantDigits"];
257     }
258     if (configs.count("maximumSignificantDigits") > 0) {
259         maximumSignificantDigits = configs["maximumSignificantDigits"];
260     }
261     if (configs.count("numberingSystem") > 0) {
262         numberingSystem = configs["numberingSystem"];
263     }
264     if (configs.count("useGrouping") > 0) {
265         useGrouping = configs["useGrouping"];
266     }
267     if (configs.count("localeMatcher") > 0) {
268         localeMatcher = configs["localeMatcher"];
269     }
270 }
271 
SetUnit(std::string & preferredUnit)272 void NumberFormat::SetUnit(std::string &preferredUnit)
273 {
274     if (preferredUnit.empty()) {
275         return;
276     }
277     for (icu::MeasureUnit curUnit : unitArray) {
278         if (!strcmp(curUnit.getSubtype(), preferredUnit.c_str())) {
279             numberFormat = numberFormat.unit(curUnit);
280         }
281     }
282 }
283 
Format(double number)284 std::string NumberFormat::Format(double number)
285 {
286     if (!createSuccess) {
287         return "";
288     }
289     double finalNumber = number;
290     if (!unitUsage.empty()) {
291         std::vector<std::string> preferredUnits;
292         if (unitUsage == "default") {
293             GetDefaultPreferredUnit(localeInfo->GetRegion(), unitType, preferredUnits);
294         } else {
295             GetPreferredUnit(localeInfo->GetRegion(), unitUsage, preferredUnits);
296         }
297         std::map<double, std::string> preferredValuesOverOne;
298         std::map<double, std::string> preferredValuesUnderOne;
299         double num = number;
300         for (size_t i = 0; i < preferredUnits.size(); i++) {
301             int status = Convert(num, unit, unitMeasSys, preferredUnits[i], unitMeasSys);
302             if (!status) {
303                 continue;
304             }
305             if (num >= 1) {
306                 preferredValuesOverOne.insert(std::make_pair(num, preferredUnits[i]));
307             } else {
308                 preferredValuesUnderOne.insert(std::make_pair(num, preferredUnits[i]));
309             }
310         }
311         std::string preferredUnit;
312         if (preferredValuesOverOne.size() > 0) {
313             finalNumber = preferredValuesOverOne.begin()->first;
314             preferredUnit = preferredValuesOverOne.begin()->second;
315         } else if (preferredValuesUnderOne.size() > 0) {
316             finalNumber = preferredValuesUnderOne.rbegin()->first;
317             preferredUnit = preferredValuesUnderOne.rbegin()->second;
318         }
319         SetUnit(preferredUnit);
320     }
321     std::string result;
322     UErrorCode status = U_ZERO_ERROR;
323     numberFormat.formatDouble(finalNumber, status).toString(status).toUTF8String(result);
324     return result;
325 }
326 
GetResolvedOptions(std::map<std::string,std::string> & map)327 void NumberFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
328 {
329     map.insert(std::make_pair("locale", localeBaseName));
330     if (!styleString.empty()) {
331         map.insert(std::make_pair("style", styleString));
332     }
333     if (!currency.empty()) {
334         map.insert(std::make_pair("currency", currency));
335     }
336     if (!currencySign.empty()) {
337         map.insert(std::make_pair("currencySign", currencySign));
338     }
339     if (!currencyDisplayString.empty()) {
340         map.insert(std::make_pair("currencyDisplay", currencyDisplayString));
341     }
342     if (!signDisplayString.empty()) {
343         map.insert(std::make_pair("signDisplay", signDisplayString));
344     }
345     if (!compactDisplay.empty()) {
346         map.insert(std::make_pair("compactDisplay", compactDisplay));
347     }
348     if (!unitDisplayString.empty()) {
349         map.insert(std::make_pair("unitDisplay", unitDisplayString));
350     }
351     if (!unitUsage.empty()) {
352         map.insert(std::make_pair("unitUsage", unitUsage));
353     }
354     if (!unit.empty()) {
355         map.insert(std::make_pair("unit", unit));
356     }
357     GetDigitsResolvedOptions(map);
358 }
359 
GetDigitsResolvedOptions(std::map<std::string,std::string> & map)360 void NumberFormat::GetDigitsResolvedOptions(std::map<std::string, std::string> &map)
361 {
362     if (!numberingSystem.empty()) {
363         map.insert(std::make_pair("numberingSystem", numberingSystem));
364     } else if (!(localeInfo->GetNumberingSystem()).empty()) {
365         map.insert(std::make_pair("numberingSystem", localeInfo->GetNumberingSystem()));
366     } else {
367         UErrorCode status = U_ZERO_ERROR;
368         auto numSys = std::unique_ptr<icu::NumberingSystem>(icu::NumberingSystem::createInstance(locale, status));
369         if (U_SUCCESS(status)) {
370             map.insert(std::make_pair("numberingSystem", numSys->getName()));
371         }
372     }
373     if (!useGrouping.empty()) {
374         map.insert(std::make_pair("useGrouping", useGrouping));
375     }
376     if (!minimumIntegerDigits.empty()) {
377         map.insert(std::make_pair("minimumIntegerDigits", minimumIntegerDigits));
378     }
379     if (!minimumFractionDigits.empty()) {
380         map.insert(std::make_pair("minimumFractionDigits", minimumFractionDigits));
381     }
382     if (!maximumFractionDigits.empty()) {
383         map.insert(std::make_pair("maximumFractionDigits", maximumFractionDigits));
384     }
385     if (!minimumSignificantDigits.empty()) {
386         map.insert(std::make_pair("minimumSignificantDigits", minimumSignificantDigits));
387     }
388     if (!maximumSignificantDigits.empty()) {
389         map.insert(std::make_pair("maximumSignificantDigits", maximumSignificantDigits));
390     }
391     if (!localeMatcher.empty()) {
392         map.insert(std::make_pair("localeMatcher", localeMatcher));
393     }
394     if (!notationString.empty()) {
395         map.insert(std::make_pair("notation", notationString));
396     }
397 }
398 
GetCurrency() const399 std::string NumberFormat::GetCurrency() const
400 {
401     return currency;
402 }
403 
GetCurrencySign() const404 std::string NumberFormat::GetCurrencySign() const
405 {
406     return currencySign;
407 }
408 
GetStyle() const409 std::string NumberFormat::GetStyle() const
410 {
411     return styleString;
412 }
413 
GetNumberingSystem() const414 std::string NumberFormat::GetNumberingSystem() const
415 {
416     return numberingSystem;
417 }
418 
GetUseGrouping() const419 std::string NumberFormat::GetUseGrouping() const
420 {
421     return useGrouping;
422 }
423 
GetMinimumIntegerDigits() const424 std::string NumberFormat::GetMinimumIntegerDigits() const
425 {
426     return minimumIntegerDigits;
427 }
428 
GetMinimumFractionDigits() const429 std::string NumberFormat::GetMinimumFractionDigits() const
430 {
431     return minimumFractionDigits;
432 }
433 
GetMaximumFractionDigits() const434 std::string NumberFormat::GetMaximumFractionDigits() const
435 {
436     return maximumFractionDigits;
437 }
438 
GetMinimumSignificantDigits() const439 std::string NumberFormat::GetMinimumSignificantDigits() const
440 {
441     return minimumSignificantDigits;
442 }
443 
GetMaximumSignificantDigits() const444 std::string NumberFormat::GetMaximumSignificantDigits() const
445 {
446     return maximumSignificantDigits;
447 }
448 
GetLocaleMatcher() const449 std::string NumberFormat::GetLocaleMatcher() const
450 {
451     return localeMatcher;
452 }
453 
Init()454 bool NumberFormat::Init()
455 {
456     SetHwIcuDirectory();
457     return true;
458 }
459 } // namespace I18n
460 } // namespace Global
461 } // namespace OHOS
462