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
16 #include "plural_format_impl.h"
17 #include <cmath>
18 #include "str_util.h"
19
20 using namespace OHOS::I18N;
21
PluralFormatImpl(LocaleInfo & locale,I18nStatus & status)22 PluralFormatImpl::PluralFormatImpl(LocaleInfo &locale, I18nStatus &status)
23 {
24 if (locale.GetId() == nullptr) {
25 status = IERROR;
26 return;
27 }
28 mLocale = locale;
29 }
30
~PluralFormatImpl()31 PluralFormatImpl::~PluralFormatImpl()
32 {
33 if (mPluralRules != nullptr) {
34 delete mPluralRules;
35 mPluralRules = nullptr;
36 }
37 if (mDecimalPluralRules != nullptr) {
38 delete mDecimalPluralRules;
39 mDecimalPluralRules = nullptr;
40 }
41 }
42
Init(const DataResource & resource)43 bool PluralFormatImpl::Init(const DataResource &resource)
44 {
45 std::string unprocessedPluralData;
46 resource.GetString(DataResourceType::PLURAL, unprocessedPluralData);
47 std::string unprocessedDecimalPluralData;
48 resource.GetString(DataResourceType::DECIMAL_PLURAL, unprocessedDecimalPluralData);
49 mPluralRules = InitPluralRules(unprocessedPluralData);
50 mDecimalPluralRules = InitPluralRules(unprocessedDecimalPluralData);
51 if (mPluralRules == nullptr || mDecimalPluralRules == nullptr) {
52 return false;
53 }
54 return true;
55 }
56
InitPluralRules(std::string unprocessedPluralData)57 PluralRules *PluralFormatImpl::InitPluralRules(std::string unprocessedPluralData)
58 {
59 std::string rules[RULES_NUM];
60 Split(unprocessedPluralData, rules, RULES_NUM, PLURAL_SEP);
61 int zeroRuleSize = static_cast<int>(rules[PluralRuleType::ZERO].size());
62 int oneRuleSize = static_cast<int>(rules[PluralRuleType::ONE].size());
63 int twoRuleSize = static_cast<int>(rules[PluralRuleType::TWO].size());
64 int fewRuleSize = static_cast<int>(rules[PluralRuleType::FEW].size());
65 int manyRuleSize = static_cast<int>(rules[PluralRuleType::MANY].size());
66 int otherRuleSize = static_cast<int>(rules[PluralRuleType::OTHER].size());
67 int ruleSizes[RULES_NUM] = { zeroRuleSize, oneRuleSize, twoRuleSize, fewRuleSize, manyRuleSize, otherRuleSize };
68 return new PluralRules(rules, RULES_NUM, ruleSizes, RULES_NUM);
69 }
70
GetPluralData(I18nStatus status) const71 PluralRules *PluralFormatImpl::GetPluralData(I18nStatus status) const
72 {
73 if (status == IERROR) {
74 return nullptr;
75 }
76 return mPluralRules;
77 }
78
GetPluralRuleIndex(double number,I18nStatus status) const79 int PluralFormatImpl::GetPluralRuleIndex(double number, I18nStatus status) const
80 {
81 if (status == IERROR) {
82 return -1;
83 }
84 if (GetPluralData(status) == nullptr) {
85 return PluralRuleType::OTHER;
86 }
87 int integerNumber = (int)number;
88 int numberInfo[NUMBER_INFO_SIZE];
89 ComputeDecimalInfo(number, integerNumber, numberInfo);
90 if ((mDecimalPluralRules->mZeroRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mZeroRule,
91 mDecimalPluralRules->mZeroRuleSize, numberInfo)) {
92 return PluralRuleType::ZERO;
93 } else if ((mDecimalPluralRules->mOneRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mOneRule,
94 mDecimalPluralRules->mOneRuleSize, numberInfo)) {
95 return PluralRuleType::ONE;
96 } else if ((mDecimalPluralRules->mTwoRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mTwoRule,
97 mDecimalPluralRules->mTwoRuleSize, numberInfo)) {
98 return PluralRuleType::TWO;
99 } else if ((mDecimalPluralRules->mFewRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mFewRule,
100 mDecimalPluralRules->mFewRuleSize, numberInfo)) {
101 return PluralRuleType::FEW;
102 } else if ((mDecimalPluralRules->mManyRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mManyRule,
103 mDecimalPluralRules->mManyRuleSize, numberInfo)) {
104 return PluralRuleType::MANY;
105 } else {
106 if (!CheckContainsIntegerRule() && numberInfo[FRACTION_NUMBER_INDEX] == 0) {
107 return GetPluralRuleIndex(integerNumber, status);
108 }
109 return PluralRuleType::OTHER;
110 }
111 }
112
CheckContainsIntegerRule() const113 bool PluralFormatImpl::CheckContainsIntegerRule() const
114 {
115 if ((mDecimalPluralRules->mZeroRuleSize > 0) &&
116 (mDecimalPluralRules->mZeroRule.find("v = 0") != std::string::npos)) {
117 return true;
118 } else if ((mDecimalPluralRules->mOneRuleSize > 0) &&
119 (mDecimalPluralRules->mOneRule.find("v = 0") != std::string::npos)) {
120 return true;
121 } else if ((mDecimalPluralRules->mTwoRuleSize > 0) &&
122 (mDecimalPluralRules->mTwoRule.find("v = 0") != std::string::npos)) {
123 return true;
124 } else if ((mDecimalPluralRules->mFewRuleSize > 0) &&
125 (mDecimalPluralRules->mFewRule.find("v = 0") != std::string::npos)) {
126 return true;
127 } else if ((mDecimalPluralRules->mManyRuleSize > 0) &&
128 (mDecimalPluralRules->mManyRule.find("v = 0") != std::string::npos)) {
129 return true;
130 } else {
131 return false;
132 }
133 }
134
ComputeDecimalInfo(double number,int integerNumber,int * numberInfo) const135 void PluralFormatImpl::ComputeDecimalInfo(double number, int integerNumber, int *numberInfo) const
136 {
137 int fractionNumber = 0;
138 int numOfFraction = 0;
139 for (int i = 1; i <= MAX_FRACTION_NUMBERS; i++) {
140 // Calculate number of fraction digits in the decimal number by judging whether
141 // multiplying by 10 is an integer.
142 double temp = number * pow(10, i);
143 if (i == MAX_FRACTION_NUMBERS && temp - ((int)temp) >= EPS) {
144 temp = round(temp);
145 }
146 if (temp - ((int)temp) < EPS) {
147 while (i > 1 && (int) temp % DECIMALISM == 0) { // 10 means decimal
148 i--;
149 temp /= DECIMALISM; // 10 means decimal
150 }
151 // 10 is base
152 fractionNumber = (int)(temp - integerNumber * pow(10, i));
153 numOfFraction = i;
154 break;
155 }
156 }
157 numberInfo[INTEGER_NUMBER_INDEX] = integerNumber;
158 numberInfo[FRACTION_NUMBER_INDEX] = fractionNumber;
159 numberInfo[NUM_OF_FRACTION_INDEX] = numOfFraction;
160 }
161
ParseDecimalRule(const std::string & rule,const int ruleSize,const int * numberInfo) const162 bool PluralFormatImpl::ParseDecimalRule(const std::string &rule, const int ruleSize, const int *numberInfo) const
163 {
164 bool tempResult = true;
165 for (int i = 0; i < ruleSize; i++) {
166 bool curResult = ParseDecimalFormula(rule, ruleSize, i, numberInfo);
167 int nextSymbolIndex = i + SYMBOL_LENGTH;
168 if (curResult && tempResult) {
169 // If next symbol is or and current result and temp result are true, the final result should be true.
170 if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == OR)) {
171 return true;
172 // If next symbol is and and current result and temp result are true, set the temp result to true and
173 // skip to next formula.
174 } else if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == AND)) {
175 i += SKIP_SYMBOL_LENGTH;
176 tempResult = true;
177 // If there is no symbol after this formula and current result and temp result are true,
178 // the final result should be true.
179 } else if (nextSymbolIndex >= ruleSize) {
180 return true;
181 }
182 } else {
183 // If next symbol is or and current result or temp result is false, skip to next formula.
184 if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == OR)) {
185 i += SKIP_SYMBOL_LENGTH;
186 tempResult = true;
187 // If next symbol is and and current result or temp result is false, skip to next formula and
188 // set temp result to false.
189 } else if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == AND)) {
190 i += SKIP_SYMBOL_LENGTH;
191 tempResult = false;
192 } else if ((nextSymbolIndex >= ruleSize) &&
193 !ParseDecimalFormula(rule, ruleSize, i, numberInfo)) {
194 tempResult = false;
195 }
196 }
197 }
198 return tempResult;
199 }
200
ParseDecimalFormula(const std::string & rule,const int ruleSize,int & index,const int * numberInfo) const201 bool PluralFormatImpl::ParseDecimalFormula(const std::string &rule, const int ruleSize, int &index,
202 const int *numberInfo) const
203 {
204 int currentNumber = 0;
205 if ((index < ruleSize) && (rule[index] == NUM_OF_FRACTION)) {
206 currentNumber = numberInfo[NUM_OF_FRACTION_INDEX];
207 } else if ((index < ruleSize) &&
208 ((rule[index] == FRACTION_NUMBER) || (rule[index] == FRACTION_NUMBER_WITH_ZERO))) {
209 currentNumber = numberInfo[FRACTION_NUMBER_INDEX];
210 } else {
211 currentNumber = numberInfo[INTEGER_NUMBER_INDEX];
212 }
213 index += SKIP_SYMBOL_LENGTH;
214 return ParseFormula(rule, ruleSize, index, currentNumber);
215 }
216
GetPluralRuleIndex(int number,I18nStatus status) const217 int PluralFormatImpl::GetPluralRuleIndex(int number, I18nStatus status) const
218 {
219 if (status == IERROR) {
220 return -1;
221 }
222 if (GetPluralData(status) == nullptr) {
223 return PluralRuleType::OTHER;
224 }
225 if ((mPluralRules->mZeroRuleSize > 0) && ParseRule(mPluralRules->mZeroRule, mPluralRules->mZeroRuleSize, number)) {
226 return PluralRuleType::ZERO;
227 } else if ((mPluralRules->mOneRuleSize > 0) &&
228 ParseRule(mPluralRules->mOneRule, mPluralRules->mOneRuleSize, number)) {
229 return PluralRuleType::ONE;
230 } else if ((mPluralRules->mTwoRuleSize > 0) &&
231 ParseRule(mPluralRules->mTwoRule, mPluralRules->mTwoRuleSize, number)) {
232 return PluralRuleType::TWO;
233 } else if ((mPluralRules->mFewRuleSize > 0) &&
234 ParseRule(mPluralRules->mFewRule, mPluralRules->mFewRuleSize, number)) {
235 return PluralRuleType::FEW;
236 } else if ((mPluralRules->mManyRuleSize > 0) &&
237 ParseRule(mPluralRules->mManyRule, mPluralRules->mManyRuleSize, number)) {
238 return PluralRuleType::MANY;
239 } else {
240 return PluralRuleType::OTHER;
241 }
242 }
243
ParseRule(const std::string & rule,const int ruleSize,const int number) const244 bool PluralFormatImpl::ParseRule(const std::string &rule, const int ruleSize, const int number) const
245 {
246 bool tempResult = true;
247 for (int i = 0; i < ruleSize; i++) {
248 bool curResult = ParseFormula(rule, ruleSize, i, number);
249 int nextSymbolIndex = i + SYMBOL_LENGTH;
250 if (curResult && tempResult) {
251 // If next symbol is or and current result and temp result are true, the final result should be true.
252 if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == OR)) {
253 return true;
254 // If next symbol is and and current result and temp result are true, set the temp result to true and
255 // skip to next formula.
256 } else if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == AND)) {
257 i += SKIP_SYMBOL_LENGTH;
258 tempResult = true;
259 // If there is no symbol after this formula and current result and temp result are true,
260 // the final result should be true.
261 } else if (nextSymbolIndex >= ruleSize) {
262 return true;
263 }
264 } else {
265 // If next symbol is or and current result or temp result is false, skip to next formula.
266 if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == OR)) {
267 i += SKIP_SYMBOL_LENGTH;
268 tempResult = true;
269 // If next symbol is and and current result or temp result is false, skip to next formula and
270 // set temp result to false.
271 } else if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == AND)) {
272 i += SKIP_SYMBOL_LENGTH;
273 tempResult = false;
274 } else if ((nextSymbolIndex >= ruleSize) && !ParseFormula(rule, ruleSize, i, number)) {
275 tempResult = false;
276 }
277 }
278 }
279 return tempResult;
280 }
281
ParseFormula(const std::string & rule,const int ruleSize,int & index,const int number) const282 bool PluralFormatImpl::ParseFormula(const std::string &rule, const int ruleSize, int &index, const int number) const
283 {
284 int currentNumber = number;
285 if ((index < ruleSize) && (rule[index] == MOD)) {
286 // Skip the module symbol and obtain the divisor number.
287 index += SKIP_SYMBOL_LENGTH;
288 int divisor = ParseNumber(rule, ruleSize, index);
289 if (divisor == 0) {
290 divisor = 1;
291 }
292 currentNumber = number % divisor;
293 index++;
294 }
295 // Compare the result with the equation
296 if (CompareResult(rule, ruleSize, index, currentNumber)) {
297 return true;
298 }
299 return false;
300 }
301
CompareResult(const std::string & rule,const int ruleSize,int & index,const int number) const302 bool PluralFormatImpl::CompareResult(const std::string &rule, const int ruleSize, int &index, const int number) const
303 {
304 if (!((index < ruleSize) && (rule[index] == EQUAL))) {
305 return CompareNotEqualResult(rule, ruleSize, index, number);
306 }
307
308 index += SKIP_SYMBOL_LENGTH;
309 int num = ParseNumber(rule, ruleSize, index);
310 bool temp = false;
311
312 // Obtain all numbers in the formula
313 while ((index < ruleSize) && ((rule[index] == COMMA) || (rule[index] == TO))) {
314 if (rule[index] == TO) {
315 // If the symbol is "to", it indicates a number range.
316 int rangeStart = num;
317 index++;
318 int rangeEnd = ParseNumber(rule, ruleSize, index);
319 if ((number >= rangeStart) && (number <= rangeEnd)) {
320 temp = true;
321 }
322 } else {
323 // Compare the input number with each number in the equation.
324 if (number == num) {
325 temp = true;
326 }
327 index++;
328 num = ParseNumber(rule, ruleSize, index);
329 }
330 }
331 if (number == num) {
332 temp = true;
333 }
334 return temp;
335 }
336
CompareNotEqualResult(const std::string & rule,const int ruleSize,int & index,const int number) const337 bool PluralFormatImpl::CompareNotEqualResult(const std::string &rule, const int ruleSize, int &index,
338 const int number) const
339 {
340 if (!((index < ruleSize) && (rule[index] == NOT_EQUAL))) {
341 return false;
342 }
343
344 index += SKIP_SYMBOL_LENGTH;
345 int num = ParseNumber(rule, ruleSize, index);
346 bool temp = true;
347
348 // Obtain all numbers in the formula
349 while ((index < ruleSize) && ((rule[index] == COMMA) || (rule[index] == TO))) {
350 if (rule[index] == TO) {
351 // If the symbol is "to", it indicates a number range.
352 index++;
353 int rangeStart = num;
354 int rangeEnd = ParseNumber(rule, ruleSize, index);
355 if ((number >= rangeStart) && (number <= rangeEnd)) {
356 temp = false;
357 }
358 } else {
359 // Compare the input number with each number in the equation.
360 if (number == num) {
361 temp = false;
362 }
363 index++;
364 num = ParseNumber(rule, ruleSize, index);
365 }
366 }
367 if (number == num) {
368 temp = false;
369 }
370 return temp;
371 }
372
ParseNumber(const std::string & rule,const int ruleSize,int & index) const373 int PluralFormatImpl::ParseNumber(const std::string &rule, const int ruleSize, int &index) const
374 {
375 int num = 0;
376
377 // Parse number in the formula.
378 while ((index < ruleSize) && (rule[index] != ' ') && (rule[index] != TO) && (rule[index] != COMMA)) {
379 num *= DECIMALISM; // 10 means decimal. Calculate decimal value of the number.
380 num += rule[index] - '0';
381 index++;
382 }
383 return num;
384 }
385