• 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()).scale(icu::number::Scale::powerOfTen(2)).precision(
121             icu::number::Precision::fixedFraction(0));
122     }
123     if (!styleString.empty() && styleString == "unit") {
124         for (icu::MeasureUnit curUnit : unitArray) {
125             if (!strcmp(curUnit.getSubtype(), unit.c_str())) {
126                 numberFormat = numberFormat.unit(curUnit);
127                 unitType = curUnit.getType();
128             }
129         }
130         UErrorCode status = U_ZERO_ERROR;
131         UMeasurementSystem measSys = ulocdata_getMeasurementSystem(localeBaseName.c_str(), &status);
132         if (U_SUCCESS(status) && measSys >= 0) {
133             unitMeasSys = measurementSystem[measSys];
134         }
135         numberFormat = numberFormat.unitWidth(unitDisplay);
136         numberFormat = numberFormat.precision(icu::number::Precision::maxFraction(DEFAULT_FRACTION_DIGITS));
137     }
138     if (!useGrouping.empty()) {
139         numberFormat = numberFormat.grouping((useGrouping == "true") ?
140             UNumberGroupingStrategy::UNUM_GROUPING_AUTO : UNumberGroupingStrategy::UNUM_GROUPING_OFF);
141     }
142     if (!currencySign.empty() || !signDisplayString.empty()) {
143         numberFormat = numberFormat.sign(signDisplay);
144     }
145     if (!notationString.empty()) {
146         numberFormat = numberFormat.notation(notation);
147     }
148     InitDigitsProperties();
149 }
150 
InitDigitsProperties()151 void NumberFormat::InitDigitsProperties()
152 {
153     int32_t status = 0;
154     if (!maximumSignificantDigits.empty() || !minimumSignificantDigits.empty()) {
155         int32_t maxSignificantDigits = ConvertString2Int(maximumSignificantDigits, status);
156         if (status == 0) {
157             numberFormat = numberFormat.precision(icu::number::Precision::maxSignificantDigits(maxSignificantDigits));
158         }
159 
160         status = 0;
161         int32_t minSignificantDigits = ConvertString2Int(minimumSignificantDigits, status);
162         if (status == 0) {
163             numberFormat = numberFormat.precision(icu::number::Precision::minSignificantDigits(minSignificantDigits));
164         }
165     } else {
166         int32_t minIntegerDigits = ConvertString2Int(minimumIntegerDigits, status);
167         if (status == 0 && minIntegerDigits > 1) {
168             numberFormat =
169                 numberFormat.integerWidth(icu::number::IntegerWidth::zeroFillTo(minIntegerDigits));
170         }
171 
172         status = 0;
173         int32_t minFractionDigits = ConvertString2Int(minimumFractionDigits, status);
174         if (status == 0) {
175             numberFormat =
176                 numberFormat.precision(icu::number::Precision::minFraction(minFractionDigits));
177         }
178 
179         status = 0;
180         int32_t maxFractionDigits = ConvertString2Int(maximumFractionDigits, status);
181         if (status == 0) {
182             numberFormat =
183                 numberFormat.precision(icu::number::Precision::maxFraction(maxFractionDigits));
184         }
185     }
186 }
187 
ParseConfigs(std::map<std::string,std::string> & configs)188 void NumberFormat::ParseConfigs(std::map<std::string, std::string> &configs)
189 {
190     if (configs.count("signDisplay") > 0) {
191         signDisplayString = configs["signDisplay"];
192     }
193     if (signAutoStyle.count(signDisplayString) > 0) {
194         signDisplay = signAutoStyle[signDisplayString];
195     }
196     if (configs.count("style") > 0) {
197         styleString = configs["style"];
198     }
199     if (styleString == "unit" && configs.count("unit") > 0) {
200         unit = configs["unit"];
201         if (configs.count("unitDisplay") > 0) {
202             unitDisplayString = configs["unitDisplay"];
203             if (unitStyle.count(unitDisplayString) > 0) {
204                 unitDisplay = unitStyle[unitDisplayString];
205             }
206         }
207         if (configs.count("unitUsage") > 0) {
208             unitUsage = configs["unitUsage"];
209         }
210     }
211     if (styleString == "currency" && configs.count("currency") > 0) {
212         currency = configs["currency"];
213         if (configs.count("currencySign") > 0) {
214             currencySign = configs["currencySign"];
215         }
216         if (currencySign.compare("accounting") == 0 && signAccountingStyle.count(signDisplayString) > 0) {
217             signDisplay = signAccountingStyle[signDisplayString];
218         }
219         if (configs.count("currencyDisplay") > 0 && currencyStyle.count(configs["currencyDisplay"]) > 0) {
220             currencyDisplayString = configs["currencyDisplay"];
221             currencyDisplay = currencyStyle[currencyDisplayString];
222         }
223     }
224     ParseDigitsConfigs(configs);
225 }
226 
ParseDigitsConfigs(std::map<std::string,std::string> & configs)227 void NumberFormat::ParseDigitsConfigs(std::map<std::string, std::string> &configs)
228 {
229     if (configs.count("notation") > 0) {
230         notationString = configs["notation"];
231         if (notationString == "scientific") {
232             notation = icu::number::Notation::scientific();
233         } else if (notationString == "engineering") {
234             notation = icu::number::Notation::engineering();
235         }
236         if (notationString == "compact" && configs.count("compactDisplay") > 0) {
237             compactDisplay = configs["compactDisplay"];
238             if (compactDisplay == "long") {
239                 notation = icu::number::Notation::compactLong();
240             } else {
241                 notation = icu::number::Notation::compactShort();
242             }
243         }
244     }
245     if (configs.count("minimumIntegerDigits") > 0) {
246         minimumIntegerDigits = configs["minimumIntegerDigits"];
247     }
248     if (configs.count("minimumFractionDigits") > 0) {
249         minimumFractionDigits = configs["minimumFractionDigits"];
250     }
251     if (configs.count("maximumFractionDigits") > 0) {
252         maximumFractionDigits = configs["maximumFractionDigits"];
253     }
254     if (configs.count("minimumSignificantDigits") > 0) {
255         minimumSignificantDigits = configs["minimumSignificantDigits"];
256     }
257     if (configs.count("maximumSignificantDigits") > 0) {
258         maximumSignificantDigits = configs["maximumSignificantDigits"];
259     }
260     if (configs.count("numberingSystem") > 0) {
261         numberingSystem = configs["numberingSystem"];
262     }
263     if (configs.count("useGrouping") > 0) {
264         useGrouping = configs["useGrouping"];
265     }
266     if (configs.count("localeMatcher") > 0) {
267         localeMatcher = configs["localeMatcher"];
268     }
269 }
270 
SetUnit(std::string & preferredUnit)271 void NumberFormat::SetUnit(std::string &preferredUnit)
272 {
273     if (preferredUnit.empty()) {
274         return;
275     }
276     for (icu::MeasureUnit curUnit : unitArray) {
277         if (!strcmp(curUnit.getSubtype(), preferredUnit.c_str())) {
278             numberFormat = numberFormat.unit(curUnit);
279         }
280     }
281 }
282 
Format(double number)283 std::string NumberFormat::Format(double number)
284 {
285     if (!createSuccess) {
286         return "";
287     }
288     double finalNumber = number;
289     if (!unitUsage.empty()) {
290         std::vector<std::string> preferredUnits;
291         if (unitUsage == "default") {
292             GetDefaultPreferredUnit(localeInfo->GetRegion(), unitType, preferredUnits);
293         } else {
294             GetPreferredUnit(localeInfo->GetRegion(), unitUsage, preferredUnits);
295         }
296         std::map<double, std::string> preferredValuesOverOne;
297         std::map<double, std::string> preferredValuesUnderOne;
298         double num = number;
299         for (size_t i = 0; i < preferredUnits.size(); i++) {
300             int status = Convert(num, unit, unitMeasSys, preferredUnits[i], unitMeasSys);
301             if (!status) {
302                 continue;
303             }
304             if (num >= 1) {
305                 preferredValuesOverOne.insert(std::make_pair(num, preferredUnits[i]));
306             } else {
307                 preferredValuesUnderOne.insert(std::make_pair(num, preferredUnits[i]));
308             }
309         }
310         std::string preferredUnit;
311         if (preferredValuesOverOne.size() > 0) {
312             finalNumber = preferredValuesOverOne.begin()->first;
313             preferredUnit = preferredValuesOverOne.begin()->second;
314         } else if (preferredValuesUnderOne.size() > 0) {
315             finalNumber = preferredValuesUnderOne.rbegin()->first;
316             preferredUnit = preferredValuesUnderOne.rbegin()->second;
317         }
318         SetUnit(preferredUnit);
319     }
320     std::string result;
321     UErrorCode status = U_ZERO_ERROR;
322     numberFormat.formatDouble(finalNumber, status).toString(status).toUTF8String(result);
323     return result;
324 }
325 
GetResolvedOptions(std::map<std::string,std::string> & map)326 void NumberFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
327 {
328     map.insert(std::make_pair("locale", localeBaseName));
329     if (!styleString.empty()) {
330         map.insert(std::make_pair("style", styleString));
331     }
332     if (!currency.empty()) {
333         map.insert(std::make_pair("currency", currency));
334     }
335     if (!currencySign.empty()) {
336         map.insert(std::make_pair("currencySign", currencySign));
337     }
338     if (!currencyDisplayString.empty()) {
339         map.insert(std::make_pair("currencyDisplay", currencyDisplayString));
340     }
341     if (!signDisplayString.empty()) {
342         map.insert(std::make_pair("signDisplay", signDisplayString));
343     }
344     if (!compactDisplay.empty()) {
345         map.insert(std::make_pair("compactDisplay", compactDisplay));
346     }
347     if (!unitDisplayString.empty()) {
348         map.insert(std::make_pair("unitDisplay", unitDisplayString));
349     }
350     if (!unitUsage.empty()) {
351         map.insert(std::make_pair("unitUsage", unitUsage));
352     }
353     if (!unit.empty()) {
354         map.insert(std::make_pair("unit", unit));
355     }
356     GetDigitsResolvedOptions(map);
357 }
358 
GetDigitsResolvedOptions(std::map<std::string,std::string> & map)359 void NumberFormat::GetDigitsResolvedOptions(std::map<std::string, std::string> &map)
360 {
361     if (!numberingSystem.empty()) {
362         map.insert(std::make_pair("numberingSystem", numberingSystem));
363     } else if (!(localeInfo->GetNumberingSystem()).empty()) {
364         map.insert(std::make_pair("numberingSystem", localeInfo->GetNumberingSystem()));
365     } else {
366         UErrorCode status = U_ZERO_ERROR;
367         auto numSys = std::unique_ptr<icu::NumberingSystem>(icu::NumberingSystem::createInstance(locale, status));
368         if (U_SUCCESS(status)) {
369             map.insert(std::make_pair("numberingSystem", numSys->getName()));
370         }
371     }
372     if (!useGrouping.empty()) {
373         map.insert(std::make_pair("useGrouping", useGrouping));
374     }
375     if (!minimumIntegerDigits.empty()) {
376         map.insert(std::make_pair("minimumIntegerDigits", minimumIntegerDigits));
377     }
378     if (!minimumFractionDigits.empty()) {
379         map.insert(std::make_pair("minimumFractionDigits", minimumFractionDigits));
380     }
381     if (!maximumFractionDigits.empty()) {
382         map.insert(std::make_pair("maximumFractionDigits", maximumFractionDigits));
383     }
384     if (!minimumSignificantDigits.empty()) {
385         map.insert(std::make_pair("minimumSignificantDigits", minimumSignificantDigits));
386     }
387     if (!maximumSignificantDigits.empty()) {
388         map.insert(std::make_pair("maximumSignificantDigits", maximumSignificantDigits));
389     }
390     if (!localeMatcher.empty()) {
391         map.insert(std::make_pair("localeMatcher", localeMatcher));
392     }
393     if (!notationString.empty()) {
394         map.insert(std::make_pair("notation", notationString));
395     }
396 }
397 
GetCurrency() const398 std::string NumberFormat::GetCurrency() const
399 {
400     return currency;
401 }
402 
GetCurrencySign() const403 std::string NumberFormat::GetCurrencySign() const
404 {
405     return currencySign;
406 }
407 
GetStyle() const408 std::string NumberFormat::GetStyle() const
409 {
410     return styleString;
411 }
412 
GetNumberingSystem() const413 std::string NumberFormat::GetNumberingSystem() const
414 {
415     return numberingSystem;
416 }
417 
GetUseGrouping() const418 std::string NumberFormat::GetUseGrouping() const
419 {
420     return useGrouping;
421 }
422 
GetMinimumIntegerDigits() const423 std::string NumberFormat::GetMinimumIntegerDigits() const
424 {
425     return minimumIntegerDigits;
426 }
427 
GetMinimumFractionDigits() const428 std::string NumberFormat::GetMinimumFractionDigits() const
429 {
430     return minimumFractionDigits;
431 }
432 
GetMaximumFractionDigits() const433 std::string NumberFormat::GetMaximumFractionDigits() const
434 {
435     return maximumFractionDigits;
436 }
437 
GetMinimumSignificantDigits() const438 std::string NumberFormat::GetMinimumSignificantDigits() const
439 {
440     return minimumSignificantDigits;
441 }
442 
GetMaximumSignificantDigits() const443 std::string NumberFormat::GetMaximumSignificantDigits() const
444 {
445     return maximumSignificantDigits;
446 }
447 
GetLocaleMatcher() const448 std::string NumberFormat::GetLocaleMatcher() const
449 {
450     return localeMatcher;
451 }
452 
Init()453 bool NumberFormat::Init()
454 {
455     SetHwIcuDirectory();
456     return true;
457 }
458 } // namespace I18n
459 } // namespace Global
460 } // namespace OHOS
461