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 "plural_rules.h"
16
17#include <unicode/stringpiece.h>
18
19#include "algorithm"
20#include "i18n_hilog.h"
21#include "locale_config.h"
22#include "unicode/locid.h"
23#include "plural_rules.h"
24#include "map"
25#include "set"
26#include "string"
27#include "unicode/unistr.h"
28#include "unicode/upluralrules.h"
29#include "utility"
30#include "utils.h"
31#include "unicode/utypes.h"
32#include "vector"
33
34namespace OHOS {
35namespace Global {
36namespace I18n {
37std::string PluralRules::ParseOption(std::map<std::string, std::string> &options, const std::string &key)
38{
39    std::map<std::string, std::string>::iterator it = options.find(key);
40    if (it != options.end()) {
41        return it->second;
42    } else {
43        return "";
44    }
45}
46
47int PluralRules::GetValidInteger(std::string &integerStr, int minValue, int maxValue, int defaultValue)
48{
49    int status = 0;
50    int validInteger = ConvertString2Int(integerStr, status);
51    if (status < 0) {
52        validInteger = defaultValue;
53    }
54    if (validInteger < minValue) {
55        validInteger = minValue;
56    }
57    if (validInteger > maxValue) {
58        validInteger = maxValue;
59    }
60    return validInteger;
61}
62
63void PluralRules::ParseAllOptions(std::map<std::string, std::string> &options)
64{
65    localeMatcher = ParseOption(options, "localeMatcher");
66    localeMatcher = (localeMatcher == "") ? "best fit" : localeMatcher;
67    type = ParseOption(options, "type");
68    type = (type == "") ? "cardinal" : type;
69    std::string minIntegerStr = ParseOption(options, "minimumIntegerDigits");
70    // 1 is minValue and defaultValue, 21 is maxValue
71    minInteger = GetValidInteger(minIntegerStr, 1, 21, 1);
72
73    minFraction = 0;
74    maxFraction = 0;
75    std::string minFractionStr = ParseOption(options, "minimumFractionDigits");
76    std::string maxFractionStr = ParseOption(options, "maximumFractionDigits");
77    std::string minSignificantStr = ParseOption(options, "minimumSignificantDigits");
78    std::string maxSignificantStr = ParseOption(options, "maximumSignificantDigits");
79    if (minSignificantStr != "" || maxSignificantStr != "") {
80        // 1 is minValue and defaultValue, 21 is maxValue
81        minSignificant = GetValidInteger(minSignificantStr, 1, 21, 1);
82        // 1 is minValue, 21 is maxValue and defaultValue
83        maxSignificant = GetValidInteger(maxSignificantStr, 1, 21, 21);
84    } else {
85        minSignificant = 0;
86        maxSignificant = 0;
87
88        if (minFractionStr != "" || maxFractionStr != "") {
89            // 0 is minValue and defaultValue, 20 is maxValue
90            minFraction = GetValidInteger(minFractionStr, 0, 20, 0);
91            int maxFractionDefault = std::max(3, minFraction);  // 3 is the default value of minFraction
92            int maxFractionMin = std::max(1, minFraction);  // 1 is the min value of minFraction
93            // 21 is max value
94            maxFraction = GetValidInteger(maxFractionStr, maxFractionMin, 21, maxFractionDefault);
95        } else {
96            minFraction = 0;  // 0 is the default value of minFraction.
97            maxFraction = 3;  // 3 is the default value of maxFraction
98        }
99    }
100}
101
102void PluralRules::InitPluralRules(std::vector<std::string> &localeTags,
103    std::map<std::string, std::string> &options)
104{
105    UPluralType uPluralType = (type == "cardinal") ? UPLURAL_TYPE_CARDINAL : UPLURAL_TYPE_ORDINAL;
106    UErrorCode status = UErrorCode::U_ZERO_ERROR;
107    localeTags.push_back(LocaleConfig::GetSystemLocale());
108    for (size_t i = 0; i < localeTags.size(); i++) {
109        std::string curLocale = localeTags[i];
110        locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status);
111        if (U_FAILURE(status)) {
112            status = U_ZERO_ERROR;
113            continue;
114        }
115        if (LocaleInfo::allValidLocales.count(locale.getLanguage()) > 0) {
116            localeInfo = std::make_unique<LocaleInfo>(curLocale, options);
117            if (!localeInfo->InitSuccess()) {
118                continue;
119            }
120            locale = localeInfo->GetLocale();
121            localeStr = localeInfo->GetBaseName();
122            pluralRules = icu::PluralRules::forLocale(locale, uPluralType, status);
123            if (status != UErrorCode::U_ZERO_ERROR || !pluralRules) {
124                continue;
125            }
126            createSuccess = true;
127            break;
128        }
129    }
130    if (status != UErrorCode::U_ZERO_ERROR || !pluralRules) {
131        HILOG_ERROR_I18N("PluralRules object created failed");
132        return;
133    }
134}
135
136void PluralRules::InitNumberFormatter()
137{
138    numberFormatter = icu::number::NumberFormatter::withLocale(locale).roundingMode(UNUM_ROUND_HALFUP);
139    if (minInteger > 1) {
140        numberFormatter = numberFormatter.integerWidth(icu::number::IntegerWidth::zeroFillTo(minInteger));
141    }
142
143    if (minSignificant >= 0) {
144        if (minSignificant > 0) {
145            icu::number::Precision precision = icu::number::Precision::minMaxSignificantDigits(minSignificant,
146                maxSignificant);
147            numberFormatter = numberFormatter.precision(precision);
148        } else {
149            icu::number::Precision precision = icu::number::Precision::minMaxFraction(minFraction, maxFraction);
150            numberFormatter = numberFormatter.precision(precision);
151        }
152    }
153}
154
155PluralRules::PluralRules(std::vector<std::string> &localeTags, std::map<std::string, std::string> &options)
156{
157    ParseAllOptions(options);
158    InitPluralRules(localeTags, options);
159    InitNumberFormatter();
160}
161
162PluralRules::~PluralRules()
163{
164    if (!pluralRules) {
165        delete pluralRules;
166        pluralRules = nullptr;
167    }
168}
169
170std::string PluralRules::Select(double number)
171{
172    if (!createSuccess || pluralRules == nullptr) {
173        return "other";
174    }
175    UErrorCode status = UErrorCode::U_ZERO_ERROR;
176    icu::number::FormattedNumber formattedNumber = numberFormatter.formatDouble(number, status);
177    if (status != UErrorCode::U_ZERO_ERROR) {
178        status = UErrorCode::U_ZERO_ERROR;
179        formattedNumber = numberFormatter.formatDouble(number, status);
180    }
181    icu::UnicodeString unicodeString = pluralRules->select(formattedNumber, status);
182    std::string result;
183    unicodeString.toUTF8String(result);
184    return result;
185}
186} // namespace I18n
187} // namespace Global
188} // namespace OHOS