• 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 "number_format.h"
16 #include <locale>
17 #include <codecvt>
18 #include "ohos/init_data.h"
19 #include "locale_config.h"
20 
21 namespace OHOS {
22 namespace Global {
23 namespace I18n {
24 bool NumberFormat::icuInitialized = NumberFormat::Init();
25 
26 std::unordered_map<std::string, UNumberUnitWidth> NumberFormat::unitStyle = {
27     { "long", UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME },
28     { "short", UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT },
29     { "narrow", UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW }
30 };
31 
32 std::unordered_map<std::string, UNumberUnitWidth> NumberFormat::currencyStyle = {
33     { "symbol", UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT },
34     { "code", UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE },
35     { "name", UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME },
36     { "narrowSymbol", UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW }
37 };
38 
39 std::unordered_map<std::string, UNumberSignDisplay> NumberFormat::signAutoStyle = {
40     { "auto", UNumberSignDisplay::UNUM_SIGN_AUTO },
41     { "never", UNumberSignDisplay::UNUM_SIGN_NEVER },
42     { "always", UNumberSignDisplay::UNUM_SIGN_ALWAYS },
43     { "exceptZero", UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO }
44 };
45 
46 std::unordered_map<std::string, UNumberSignDisplay> NumberFormat::signAccountingStyle = {
47     { "auto", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING },
48     { "never", UNumberSignDisplay::UNUM_SIGN_NEVER },
49     { "always", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS },
50     { "exceptZero", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO }
51 };
52 
53 std::unordered_map<UMeasurementSystem, std::string> NumberFormat::measurementSystem = {
54     { UMeasurementSystem::UMS_SI, "SI" },
55     { UMeasurementSystem::UMS_US, "US" },
56     { UMeasurementSystem::UMS_UK, "UK" },
57 };
58 
NumberFormat(const std::vector<std::string> & localeTags,std::map<std::string,std::string> & configs)59 NumberFormat::NumberFormat(const std::vector<std::string> &localeTags, std::map<std::string, std::string> &configs)
60 {
61     UErrorCode status = U_ZERO_ERROR;
62     std::unique_ptr<icu::LocaleBuilder> builder = nullptr;
63     builder = std::make_unique<icu::LocaleBuilder>();
64     ParseConfigs(configs);
65     for (size_t i = 0; i < localeTags.size(); i++) {
66         std::string curLocale = localeTags[i];
67         locale = builder->setLanguageTag(icu::StringPiece(curLocale)).build(status);
68         if (LocaleInfo::allValidLocales.count(locale.getLanguage()) > 0) {
69             localeInfo = new LocaleInfo(curLocale, configs);
70             locale = localeInfo->GetLocale();
71             localeBaseName = localeInfo->GetBaseName();
72             numberFormat = icu::number::NumberFormatter::withLocale(locale);
73             icu::MeasureUnit::getAvailable(unitArray, MAX_UNIT_NUM, status);
74             break;
75         }
76     }
77     if (!localeInfo) {
78         localeInfo = new LocaleInfo(LocaleConfig::GetSystemLocale(), configs);
79         locale = localeInfo->GetLocale();
80         localeBaseName = localeInfo->GetBaseName();
81         numberFormat = icu::number::NumberFormatter::withLocale(locale);
82         icu::MeasureUnit::getAvailable(unitArray, MAX_UNIT_NUM, status);
83     }
84     InitProperties();
85 }
86 
~NumberFormat()87 NumberFormat::~NumberFormat()
88 {
89     if (localeInfo != nullptr) {
90         delete localeInfo;
91         localeInfo = nullptr;
92     }
93 }
94 
InitProperties()95 void NumberFormat::InitProperties()
96 {
97     if (!currency.empty()) {
98         UErrorCode status = U_ZERO_ERROR;
99         numberFormat =
100             numberFormat.unit(icu::CurrencyUnit(icu::UnicodeString(currency.c_str()).getBuffer(), status));
101         if (currencyDisplay != UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT) {
102             numberFormat = numberFormat.unitWidth(currencyDisplay);
103         }
104     }
105     if (!styleString.empty() && styleString == "percent") {
106         numberFormat = numberFormat.unit(icu::NoUnit::percent());
107     }
108     if (!styleString.empty() && styleString == "unit") {
109         for (icu::MeasureUnit curUnit : unitArray) {
110             if (!strcmp(curUnit.getSubtype(), unit.c_str())) {
111                 numberFormat = numberFormat.unit(curUnit);
112                 unitType = curUnit.getType();
113             }
114         }
115         UErrorCode status = U_ZERO_ERROR;
116         UMeasurementSystem measSys = ulocdata_getMeasurementSystem(localeBaseName.c_str(), &status);
117         if (status == U_ZERO_ERROR && measSys >= 0) {
118             unitMeasSys = measurementSystem[measSys];
119         }
120         numberFormat = numberFormat.unitWidth(unitDisplay);
121         numberFormat = numberFormat.precision(icu::number::Precision::maxFraction(DEFAULT_FRACTION_DIGITS));
122     }
123     if (!useGrouping.empty()) {
124         numberFormat.grouping((useGrouping == "true") ?
125             UNumberGroupingStrategy::UNUM_GROUPING_AUTO : UNumberGroupingStrategy::UNUM_GROUPING_OFF);
126     }
127     if (!currencySign.empty() || !signDisplayString.empty()) {
128         numberFormat = numberFormat.sign(signDisplay);
129     }
130     if (!notationString.empty()) {
131         numberFormat = numberFormat.notation(notation);
132     }
133     InitDigitsProperties();
134 }
135 
InitDigitsProperties()136 void NumberFormat::InitDigitsProperties()
137 {
138     if (!maximumSignificantDigits.empty() || !minimumSignificantDigits.empty()) {
139         if (!maximumSignificantDigits.empty()) {
140             int32_t maxSignificantDigits = std::stoi(maximumSignificantDigits);
141             numberFormat = numberFormat.precision(icu::number::Precision::maxSignificantDigits(maxSignificantDigits));
142         }
143         if (!minimumSignificantDigits.empty()) {
144             int32_t minSignificantDigits = std::stoi(minimumSignificantDigits);
145             numberFormat = numberFormat.precision(icu::number::Precision::minSignificantDigits(minSignificantDigits));
146         }
147     } else {
148         if (!minimumIntegerDigits.empty() && std::stoi(minimumIntegerDigits) > 1) {
149             numberFormat =
150                 numberFormat.integerWidth(icu::number::IntegerWidth::zeroFillTo(std::stoi(minimumIntegerDigits)));
151         }
152         if (!minimumFractionDigits.empty()) {
153             numberFormat =
154                 numberFormat.precision(icu::number::Precision::minFraction(std::stoi(minimumFractionDigits)));
155         }
156         if (!maximumFractionDigits.empty()) {
157             numberFormat =
158                 numberFormat.precision(icu::number::Precision::maxFraction(std::stoi(maximumFractionDigits)));
159         }
160     }
161 }
162 
ParseConfigs(std::map<std::string,std::string> & configs)163 void NumberFormat::ParseConfigs(std::map<std::string, std::string> &configs)
164 {
165     if (configs.count("signDisplay") > 0) {
166         signDisplayString = configs["signDisplay"];
167         if (signAutoStyle.count(signDisplayString) > 0) {
168             signDisplay = signAutoStyle[signDisplayString];
169         }
170     }
171     if (configs.count("style") > 0) {
172         styleString = configs["style"];
173     }
174     if (styleString == "unit" && configs.count("unit") > 0) {
175         unit = configs["unit"];
176         if (configs.count("unitDisplay") > 0) {
177             unitDisplayString = configs["unitDisplay"];
178             if (unitStyle.count(unitDisplayString) > 0) {
179                 unitDisplay = unitStyle[unitDisplayString];
180             }
181         }
182         if (configs.count("unitUsage") > 0) {
183             unitUsage = configs["unitUsage"];
184         }
185     }
186     if (styleString == "currency" && configs.count("currency") > 0) {
187         currency = configs["currency"];
188         if (configs.count("currencySign") > 0) {
189             currencySign = configs["currencySign"];
190             if (configs["currencySign"] != "accounting" && !signDisplayString.empty()) {
191                 signDisplay = signAccountingStyle[signDisplayString];
192             }
193         }
194         if (configs.count("currencyDisplay") > 0 && currencyStyle.count(configs["currencyDisplay"]) > 0) {
195             currencyDisplayString = configs["currencyDisplay"];
196             currencyDisplay = currencyStyle[currencyDisplayString];
197         }
198     }
199     ParseDigitsConfigs(configs);
200 }
201 
ParseDigitsConfigs(std::map<std::string,std::string> & configs)202 void NumberFormat::ParseDigitsConfigs(std::map<std::string, std::string> &configs)
203 {
204     if (configs.count("notation") > 0) {
205         notationString = configs["notation"];
206         if (notationString == "scientific") {
207             notation = icu::number::Notation::scientific();
208         } else if (notationString == "engineering") {
209             notation = icu::number::Notation::engineering();
210         }
211         if (notationString == "compact" && configs.count("compactDisplay") > 0) {
212             compactDisplay = configs["compactDisplay"];
213             if (compactDisplay == "long") {
214                 notation = icu::number::Notation::compactLong();
215             } else {
216                 notation = icu::number::Notation::compactShort();
217             }
218         }
219     }
220     if (configs.count("minimumIntegerDigits") > 0) {
221         minimumIntegerDigits = configs["minimumIntegerDigits"];
222     }
223     if (configs.count("minimumFractionDigits") > 0) {
224         minimumFractionDigits = configs["minimumFractionDigits"];
225     }
226     if (configs.count("maximumFractionDigits") > 0) {
227         maximumFractionDigits = configs["maximumFractionDigits"];
228     }
229     if (configs.count("minimumSignificantDigits") > 0) {
230         minimumSignificantDigits = configs["minimumSignificantDigits"];
231     }
232     if (configs.count("maximumSignificantDigits") > 0) {
233         maximumSignificantDigits = configs["maximumSignificantDigits"];
234     }
235     if (configs.count("numberingSystem") > 0) {
236         numberingSystem = configs["numberingSystem"];
237     }
238     if (configs.count("useGrouping") > 0) {
239         useGrouping = configs["useGrouping"];
240     }
241     if (configs.count("localeMatcher") > 0) {
242         localeMatcher = configs["localeMatcher"];
243     }
244 }
245 
Format(double number)246 std::string NumberFormat::Format(double number)
247 {
248     double finalNumber = number;
249     if (!unitUsage.empty()) {
250         std::vector<std::string> preferredUnits;
251         if (unitUsage == "default") {
252             GetDefaultPreferredUnit(localeInfo->GetRegion(), unitType, preferredUnits);
253         } else {
254             GetPreferredUnit(localeInfo->GetRegion(), unitUsage, preferredUnits);
255         }
256         std::map<double, std::string> preferredValuesOverOne;
257         std::map<double, std::string> preferredValuesUnderOne;
258         double num = number;
259         for (size_t i = 0; i < preferredUnits.size(); i++) {
260             int status = Convert(num, unit, unitMeasSys, preferredUnits[i], unitMeasSys);
261             if (!status) {
262                 continue;
263             }
264             if (num >= 1) {
265                 preferredValuesOverOne.insert(std::make_pair(num, preferredUnits[i]));
266             } else {
267                 preferredValuesUnderOne.insert(std::make_pair(num, preferredUnits[i]));
268             }
269         }
270         std::string preferredUnit;
271         if (preferredValuesOverOne.size() > 0) {
272             finalNumber = preferredValuesOverOne.begin()->first;
273             preferredUnit = preferredValuesOverOne.begin()->second;
274         } else if (preferredValuesUnderOne.size() > 0) {
275             finalNumber = preferredValuesUnderOne.rbegin()->first;
276             preferredUnit = preferredValuesUnderOne.rbegin()->second;
277         }
278         if (!preferredUnit.empty()) {
279             for (icu::MeasureUnit curUnit : unitArray) {
280                 if (!strcmp(curUnit.getSubtype(), preferredUnit.c_str())) {
281                     numberFormat = numberFormat.unit(curUnit);
282                 }
283             }
284         }
285     }
286     std::string result;
287     UErrorCode status = U_ZERO_ERROR;
288     numberFormat.formatDouble(finalNumber, status).toString(status).toUTF8String(result);
289     return result;
290 }
291 
GetResolvedOptions(std::map<std::string,std::string> & map)292 void NumberFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
293 {
294     map.insert(std::make_pair("locale", localeBaseName));
295     if (!styleString.empty()) {
296         map.insert(std::make_pair("style", styleString));
297     }
298     if (!currency.empty()) {
299         map.insert(std::make_pair("currency", currency));
300     }
301     if (!currencySign.empty()) {
302         map.insert(std::make_pair("currencySign", currencySign));
303     }
304     if (!currencyDisplayString.empty()) {
305         map.insert(std::make_pair("currencyDisplay", currencyDisplayString));
306     }
307     if (!signDisplayString.empty()) {
308         map.insert(std::make_pair("signDisplay", signDisplayString));
309     }
310     if (!compactDisplay.empty()) {
311         map.insert(std::make_pair("compactDisplay", compactDisplay));
312     }
313     if (!unitDisplayString.empty()) {
314         map.insert(std::make_pair("unitDisplay", unitDisplayString));
315     }
316     if (!unitUsage.empty()) {
317         map.insert(std::make_pair("unitUsage", unitUsage));
318     }
319     if (!unit.empty()) {
320         map.insert(std::make_pair("unit", unit));
321     }
322     GetDigitsResolvedOptions(map);
323 }
324 
GetDigitsResolvedOptions(std::map<std::string,std::string> & map)325 void NumberFormat::GetDigitsResolvedOptions(std::map<std::string, std::string> &map)
326 {
327     UErrorCode status = U_ZERO_ERROR;
328     if (!numberingSystem.empty()) {
329         map.insert(std::make_pair("numberingSystem", numberingSystem));
330     } else if (!(localeInfo->GetNumberingSystem()).empty()) {
331         map.insert(std::make_pair("numberingSystem", localeInfo->GetNumberingSystem()));
332     } else {
333         auto numSys = std::unique_ptr<icu::NumberingSystem>(icu::NumberingSystem::createInstance(locale, status));
334         map.insert(std::make_pair("numberingSystem", numSys->getName()));
335     }
336     if (!useGrouping.empty()) {
337         map.insert(std::make_pair("useGrouping", useGrouping));
338     }
339     if (!minimumIntegerDigits.empty()) {
340         map.insert(std::make_pair("minimumIntegerDigits", minimumIntegerDigits));
341     }
342     if (!minimumFractionDigits.empty()) {
343         map.insert(std::make_pair("minimumFractionDigits", minimumFractionDigits));
344     }
345     if (!maximumFractionDigits.empty()) {
346         map.insert(std::make_pair("maximumFractionDigits", maximumFractionDigits));
347     }
348     if (!minimumSignificantDigits.empty()) {
349         map.insert(std::make_pair("minimumSignificantDigits", minimumSignificantDigits));
350     }
351     if (!maximumSignificantDigits.empty()) {
352         map.insert(std::make_pair("maximumSignificantDigits", maximumSignificantDigits));
353     }
354     if (!localeMatcher.empty()) {
355         map.insert(std::make_pair("localeMatcher", localeMatcher));
356     }
357     if (!notationString.empty()) {
358         map.insert(std::make_pair("notation", notationString));
359     }
360 }
361 
GetCurrency() const362 std::string NumberFormat::GetCurrency() const
363 {
364     return currency;
365 }
366 
GetCurrencySign() const367 std::string NumberFormat::GetCurrencySign() const
368 {
369     return currencySign;
370 }
371 
GetStyle() const372 std::string NumberFormat::GetStyle() const
373 {
374     return styleString;
375 }
376 
GetNumberingSystem() const377 std::string NumberFormat::GetNumberingSystem() const
378 {
379     return numberingSystem;
380 }
381 
GetUseGrouping() const382 std::string NumberFormat::GetUseGrouping() const
383 {
384     return useGrouping;
385 }
386 
GetMinimumIntegerDigits() const387 std::string NumberFormat::GetMinimumIntegerDigits() const
388 {
389     return minimumIntegerDigits;
390 }
391 
GetMinimumFractionDigits() const392 std::string NumberFormat::GetMinimumFractionDigits() const
393 {
394     return minimumFractionDigits;
395 }
396 
GetMaximumFractionDigits() const397 std::string NumberFormat::GetMaximumFractionDigits() const
398 {
399     return maximumFractionDigits;
400 }
401 
GetMinimumSignificantDigits() const402 std::string NumberFormat::GetMinimumSignificantDigits() const
403 {
404     return minimumSignificantDigits;
405 }
406 
GetMaximumSignificantDigits() const407 std::string NumberFormat::GetMaximumSignificantDigits() const
408 {
409     return maximumSignificantDigits;
410 }
411 
GetLocaleMatcher() const412 std::string NumberFormat::GetLocaleMatcher() const
413 {
414     return localeMatcher;
415 }
416 
Init()417 bool NumberFormat::Init()
418 {
419     SetHwIcuDirectory();
420     return true;
421 }
422 } // namespace I18n
423 } // namespace Global
424 } // namespace OHOS
425