• 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     // 2 is the power of ten
121         numberFormat = numberFormat.unit(icu::NoUnit::percent()).scale(icu::number::Scale::powerOfTen(2)).precision(
122             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         int32_t minFdStatus = 0;
174         int32_t minFractionDigits = ConvertString2Int(minimumFractionDigits, minFdStatus);
175         int32_t maxFdStatus = 0;
176         int32_t maxFractionDigits = ConvertString2Int(maximumFractionDigits, maxFdStatus);
177         if (minFdStatus == 0 && maxFdStatus != 0) {
178             numberFormat =
179                 numberFormat.precision(icu::number::Precision::minFraction(minFractionDigits));
180         } else if (minFdStatus != 0 && maxFdStatus == 0) {
181             numberFormat =
182                 numberFormat.precision(icu::number::Precision::maxFraction(maxFractionDigits));
183         } else if (minFdStatus == 0 && maxFdStatus == 0) {
184             numberFormat =
185                 numberFormat.precision(icu::number::Precision::minMaxFraction(minFractionDigits, maxFractionDigits));
186         }
187     }
188 }
189 
ParseConfigs(std::map<std::string,std::string> & configs)190 void NumberFormat::ParseConfigs(std::map<std::string, std::string> &configs)
191 {
192     if (configs.count("signDisplay") > 0) {
193         signDisplayString = configs["signDisplay"];
194     }
195     if (signAutoStyle.count(signDisplayString) > 0) {
196         signDisplay = signAutoStyle[signDisplayString];
197     }
198     if (configs.count("style") > 0) {
199         styleString = configs["style"];
200     }
201     if (styleString == "unit" && configs.count("unit") > 0) {
202         unit = configs["unit"];
203         if (configs.count("unitDisplay") > 0) {
204             unitDisplayString = configs["unitDisplay"];
205             if (unitStyle.count(unitDisplayString) > 0) {
206                 unitDisplay = unitStyle[unitDisplayString];
207             }
208         }
209         if (configs.count("unitUsage") > 0) {
210             unitUsage = configs["unitUsage"];
211         }
212     }
213     if (styleString == "currency" && configs.count("currency") > 0) {
214         currency = configs["currency"];
215         if (configs.count("currencySign") > 0) {
216             currencySign = configs["currencySign"];
217         }
218         if (currencySign.compare("accounting") == 0 && signAccountingStyle.count(signDisplayString) > 0) {
219             signDisplay = signAccountingStyle[signDisplayString];
220         }
221         if (configs.count("currencyDisplay") > 0 && currencyStyle.count(configs["currencyDisplay"]) > 0) {
222             currencyDisplayString = configs["currencyDisplay"];
223             currencyDisplay = currencyStyle[currencyDisplayString];
224         }
225     }
226     ParseDigitsConfigs(configs);
227 }
228 
ParseDigitsConfigs(std::map<std::string,std::string> & configs)229 void NumberFormat::ParseDigitsConfigs(std::map<std::string, std::string> &configs)
230 {
231     if (configs.count("notation") > 0) {
232         notationString = configs["notation"];
233         if (notationString == "scientific") {
234             notation = icu::number::Notation::scientific();
235         } else if (notationString == "engineering") {
236             notation = icu::number::Notation::engineering();
237         }
238         if (notationString == "compact") {
239             if (configs.count("compactDisplay") > 0) {
240                 compactDisplay = configs["compactDisplay"];
241             }
242             if (compactDisplay == "long") {
243                 notation = icu::number::Notation::compactLong();
244             } else {
245                 notation = icu::number::Notation::compactShort();
246             }
247         }
248     }
249     if (configs.count("minimumIntegerDigits") > 0) {
250         minimumIntegerDigits = configs["minimumIntegerDigits"];
251     }
252     if (configs.count("minimumFractionDigits") > 0) {
253         minimumFractionDigits = configs["minimumFractionDigits"];
254     }
255     if (configs.count("maximumFractionDigits") > 0) {
256         maximumFractionDigits = configs["maximumFractionDigits"];
257     }
258     if (configs.count("minimumSignificantDigits") > 0) {
259         minimumSignificantDigits = configs["minimumSignificantDigits"];
260     }
261     if (configs.count("maximumSignificantDigits") > 0) {
262         maximumSignificantDigits = configs["maximumSignificantDigits"];
263     }
264     if (configs.count("numberingSystem") > 0) {
265         numberingSystem = configs["numberingSystem"];
266     }
267     if (configs.count("useGrouping") > 0) {
268         useGrouping = configs["useGrouping"];
269     }
270     if (configs.count("localeMatcher") > 0) {
271         localeMatcher = configs["localeMatcher"];
272     }
273 }
274 
SetUnit(std::string & preferredUnit)275 void NumberFormat::SetUnit(std::string &preferredUnit)
276 {
277     if (preferredUnit.empty()) {
278         return;
279     }
280     for (icu::MeasureUnit curUnit : unitArray) {
281         if (!strcmp(curUnit.getSubtype(), preferredUnit.c_str())) {
282             numberFormat = numberFormat.unit(curUnit);
283         }
284     }
285 }
286 
Format(double number)287 std::string NumberFormat::Format(double number)
288 {
289     if (!createSuccess) {
290         return "";
291     }
292     double finalNumber = number;
293     if (!unitUsage.empty()) {
294         std::vector<std::string> preferredUnits;
295         if (unitUsage == "default") {
296             GetDefaultPreferredUnit(localeInfo->GetRegion(), unitType, preferredUnits);
297         } else {
298             GetPreferredUnit(localeInfo->GetRegion(), unitUsage, preferredUnits);
299         }
300         std::map<double, std::string> preferredValuesOverOne;
301         std::map<double, std::string> preferredValuesUnderOne;
302         double num = number;
303         for (size_t i = 0; i < preferredUnits.size(); i++) {
304             int status = Convert(num, unit, unitMeasSys, preferredUnits[i], unitMeasSys);
305             if (!status) {
306                 continue;
307             }
308             if (num >= 1) {
309                 preferredValuesOverOne.insert(std::make_pair(num, preferredUnits[i]));
310             } else {
311                 preferredValuesUnderOne.insert(std::make_pair(num, preferredUnits[i]));
312             }
313         }
314         std::string preferredUnit;
315         if (preferredValuesOverOne.size() > 0) {
316             finalNumber = preferredValuesOverOne.begin()->first;
317             preferredUnit = preferredValuesOverOne.begin()->second;
318         } else if (preferredValuesUnderOne.size() > 0) {
319             finalNumber = preferredValuesUnderOne.rbegin()->first;
320             preferredUnit = preferredValuesUnderOne.rbegin()->second;
321         }
322         SetUnit(preferredUnit);
323     }
324     std::string result;
325     UErrorCode status = U_ZERO_ERROR;
326     numberFormat.formatDouble(finalNumber, status).toString(status).toUTF8String(result);
327     return result;
328 }
329 
GetResolvedOptions(std::map<std::string,std::string> & map)330 void NumberFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
331 {
332     map.insert(std::make_pair("locale", localeBaseName));
333     if (!styleString.empty()) {
334         map.insert(std::make_pair("style", styleString));
335     }
336     if (!currency.empty()) {
337         map.insert(std::make_pair("currency", currency));
338     }
339     if (!currencySign.empty()) {
340         map.insert(std::make_pair("currencySign", currencySign));
341     }
342     if (!currencyDisplayString.empty()) {
343         map.insert(std::make_pair("currencyDisplay", currencyDisplayString));
344     }
345     if (!signDisplayString.empty()) {
346         map.insert(std::make_pair("signDisplay", signDisplayString));
347     }
348     if (!compactDisplay.empty()) {
349         map.insert(std::make_pair("compactDisplay", compactDisplay));
350     }
351     if (!unitDisplayString.empty()) {
352         map.insert(std::make_pair("unitDisplay", unitDisplayString));
353     }
354     if (!unitUsage.empty()) {
355         map.insert(std::make_pair("unitUsage", unitUsage));
356     }
357     if (!unit.empty()) {
358         map.insert(std::make_pair("unit", unit));
359     }
360     GetDigitsResolvedOptions(map);
361 }
362 
GetDigitsResolvedOptions(std::map<std::string,std::string> & map)363 void NumberFormat::GetDigitsResolvedOptions(std::map<std::string, std::string> &map)
364 {
365     if (!numberingSystem.empty()) {
366         map.insert(std::make_pair("numberingSystem", numberingSystem));
367     } else if (!(localeInfo->GetNumberingSystem()).empty()) {
368         map.insert(std::make_pair("numberingSystem", localeInfo->GetNumberingSystem()));
369     } else {
370         UErrorCode status = U_ZERO_ERROR;
371         auto numSys = std::unique_ptr<icu::NumberingSystem>(icu::NumberingSystem::createInstance(locale, status));
372         if (U_SUCCESS(status)) {
373             map.insert(std::make_pair("numberingSystem", numSys->getName()));
374         }
375     }
376     if (!useGrouping.empty()) {
377         map.insert(std::make_pair("useGrouping", useGrouping));
378     }
379     if (!minimumIntegerDigits.empty()) {
380         map.insert(std::make_pair("minimumIntegerDigits", minimumIntegerDigits));
381     }
382     if (!minimumFractionDigits.empty()) {
383         map.insert(std::make_pair("minimumFractionDigits", minimumFractionDigits));
384     }
385     if (!maximumFractionDigits.empty()) {
386         map.insert(std::make_pair("maximumFractionDigits", maximumFractionDigits));
387     }
388     if (!minimumSignificantDigits.empty()) {
389         map.insert(std::make_pair("minimumSignificantDigits", minimumSignificantDigits));
390     }
391     if (!maximumSignificantDigits.empty()) {
392         map.insert(std::make_pair("maximumSignificantDigits", maximumSignificantDigits));
393     }
394     if (!localeMatcher.empty()) {
395         map.insert(std::make_pair("localeMatcher", localeMatcher));
396     }
397     if (!notationString.empty()) {
398         map.insert(std::make_pair("notation", notationString));
399     }
400 }
401 
GetCurrency() const402 std::string NumberFormat::GetCurrency() const
403 {
404     return currency;
405 }
406 
GetCurrencySign() const407 std::string NumberFormat::GetCurrencySign() const
408 {
409     return currencySign;
410 }
411 
GetStyle() const412 std::string NumberFormat::GetStyle() const
413 {
414     return styleString;
415 }
416 
GetNumberingSystem() const417 std::string NumberFormat::GetNumberingSystem() const
418 {
419     return numberingSystem;
420 }
421 
GetUseGrouping() const422 std::string NumberFormat::GetUseGrouping() const
423 {
424     return useGrouping;
425 }
426 
GetMinimumIntegerDigits() const427 std::string NumberFormat::GetMinimumIntegerDigits() const
428 {
429     return minimumIntegerDigits;
430 }
431 
GetMinimumFractionDigits() const432 std::string NumberFormat::GetMinimumFractionDigits() const
433 {
434     return minimumFractionDigits;
435 }
436 
GetMaximumFractionDigits() const437 std::string NumberFormat::GetMaximumFractionDigits() const
438 {
439     return maximumFractionDigits;
440 }
441 
GetMinimumSignificantDigits() const442 std::string NumberFormat::GetMinimumSignificantDigits() const
443 {
444     return minimumSignificantDigits;
445 }
446 
GetMaximumSignificantDigits() const447 std::string NumberFormat::GetMaximumSignificantDigits() const
448 {
449     return maximumSignificantDigits;
450 }
451 
GetLocaleMatcher() const452 std::string NumberFormat::GetLocaleMatcher() const
453 {
454     return localeMatcher;
455 }
456 
Init()457 bool NumberFormat::Init()
458 {
459     SetHwIcuDirectory();
460     return true;
461 }
462 } // namespace I18n
463 } // namespace Global
464 } // namespace OHOS
465