• 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 
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, NUMBER_INFO_SIZE);
90     if ((mDecimalPluralRules->mZeroRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mZeroRule,
91         mDecimalPluralRules->mZeroRuleSize, numberInfo, NUMBER_INFO_SIZE)) {
92         return PluralRuleType::ZERO;
93     } else if ((mDecimalPluralRules->mOneRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mOneRule,
94         mDecimalPluralRules->mOneRuleSize, numberInfo, NUMBER_INFO_SIZE)) {
95         return PluralRuleType::ONE;
96     } else if ((mDecimalPluralRules->mTwoRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mTwoRule,
97         mDecimalPluralRules->mTwoRuleSize, numberInfo, NUMBER_INFO_SIZE)) {
98         return PluralRuleType::TWO;
99     } else if ((mDecimalPluralRules->mFewRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mFewRule,
100         mDecimalPluralRules->mFewRuleSize, numberInfo, NUMBER_INFO_SIZE)) {
101         return PluralRuleType::FEW;
102     } else if ((mDecimalPluralRules->mManyRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mManyRule,
103         mDecimalPluralRules->mManyRuleSize, numberInfo, NUMBER_INFO_SIZE)) {
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,const int numberInfoSize) const135 void PluralFormatImpl::ComputeDecimalInfo(double number, int integerNumber, int *numberInfo,
136     const int numberInfoSize) const
137 {
138     int fractionNumber = 0;
139     int numOfFraction = 0;
140     for (int i = 1; i <= MAX_FRACTION_NUMBERS; i++) {
141         // Calculate number of fraction digits in the decimal number by judging whether
142         // multiplying by 10 is an integer.
143         double temp = number * pow(10, i);
144         if (i == MAX_FRACTION_NUMBERS && temp - ((int)temp) >= EPS) {
145             temp = round(temp);
146         }
147         if (temp - ((int)temp) < EPS) {
148             while (i > 1 && (int) temp % 10 == 0) { // 10 means decimal
149                 i--;
150                 temp /= 10; // 10 means decimal
151             }
152             // 10 is base
153             fractionNumber = (int)(temp - integerNumber * pow(10, i));
154             numOfFraction = i;
155             break;
156         }
157     }
158     numberInfo[INTEGER_NUMBER_INDEX] = integerNumber;
159     numberInfo[FRACTION_NUMBER_INDEX] = fractionNumber;
160     numberInfo[NUM_OF_FRACTION_INDEX] = numOfFraction;
161 }
162 
ParseDecimalRule(const std::string & rule,const int ruleSize,const int * numberInfo,const int numberInfoSize) const163 bool PluralFormatImpl::ParseDecimalRule(const std::string &rule, const int ruleSize, const int *numberInfo,
164     const int numberInfoSize) const
165 {
166     bool tempResult = true;
167     for (int i = 0; i < ruleSize; i++) {
168         bool curResult = ParseDecimalFormula(rule, ruleSize, i, numberInfo, numberInfoSize);
169         int nextSymbolIndex = i + SYMBOL_LENGTH;
170         if (curResult && tempResult) {
171             // If next symbol is or and current result and temp result are true, the final result should be true.
172             if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == OR)) {
173                 return true;
174             // If next symbol is and and current result and temp result are true, set the temp result to true and
175             // skip to next formula.
176             } else if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == AND)) {
177                 i += SKIP_SYMBOL_LENGTH;
178                 tempResult = true;
179             // If there is no symbol after this formula and current result and temp result are true,
180             // the final result should be true.
181             } else if (nextSymbolIndex >= ruleSize) {
182                 return true;
183             }
184         } else {
185             // If next symbol is or and current result or temp result is false, skip to next formula.
186             if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == OR)) {
187                 i += SKIP_SYMBOL_LENGTH;
188                 tempResult = true;
189             // If next symbol is and and current result or temp result is false, skip to next formula and
190             // set temp result to false.
191             } else if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == AND)) {
192                 i += SKIP_SYMBOL_LENGTH;
193                 tempResult = false;
194             } else if ((nextSymbolIndex >= ruleSize) &&
195                         !ParseDecimalFormula(rule, ruleSize, i, numberInfo, numberInfoSize)) {
196                 tempResult = false;
197             }
198         }
199     }
200     return tempResult;
201 }
202 
ParseDecimalFormula(const std::string & rule,const int ruleSize,int & index,const int * numberInfo,const int numberInfoSize) const203 bool PluralFormatImpl::ParseDecimalFormula(const std::string &rule, const int ruleSize, int &index,
204     const int *numberInfo, const int numberInfoSize) const
205 {
206     int currentNumber = 0;
207     if ((index < ruleSize) && (rule[index] == NUM_OF_FRACTION)) {
208         currentNumber = numberInfo[NUM_OF_FRACTION_INDEX];
209     } else if ((index < ruleSize) &&
210         ((rule[index] == FRACTION_NUMBER) || (rule[index] == FRACTION_NUMBER_WITH_ZERO))) {
211         currentNumber = numberInfo[FRACTION_NUMBER_INDEX];
212     } else {
213         currentNumber = numberInfo[INTEGER_NUMBER_INDEX];
214     }
215     index += SKIP_SYMBOL_LENGTH;
216     return ParseFormula(rule, ruleSize, index, currentNumber);
217 }
218 
GetPluralRuleIndex(int number,I18nStatus status) const219 int PluralFormatImpl::GetPluralRuleIndex(int number, I18nStatus status) const
220 {
221     if (status == IERROR) {
222         return -1;
223     }
224     if (GetPluralData(status) == nullptr) {
225         return PluralRuleType::OTHER;
226     }
227     if ((mPluralRules->mZeroRuleSize > 0) && ParseRule(mPluralRules->mZeroRule, mPluralRules->mZeroRuleSize, number)) {
228         return PluralRuleType::ZERO;
229     } else if ((mPluralRules->mOneRuleSize > 0) &&
230         ParseRule(mPluralRules->mOneRule, mPluralRules->mOneRuleSize, number)) {
231         return PluralRuleType::ONE;
232     } else if ((mPluralRules->mTwoRuleSize > 0) &&
233         ParseRule(mPluralRules->mTwoRule, mPluralRules->mTwoRuleSize, number)) {
234         return PluralRuleType::TWO;
235     } else if ((mPluralRules->mFewRuleSize > 0) &&
236         ParseRule(mPluralRules->mFewRule, mPluralRules->mFewRuleSize, number)) {
237         return PluralRuleType::FEW;
238     } else if ((mPluralRules->mManyRuleSize > 0) &&
239         ParseRule(mPluralRules->mManyRule, mPluralRules->mManyRuleSize, number)) {
240         return PluralRuleType::MANY;
241     } else {
242         return PluralRuleType::OTHER;
243     }
244 }
245 
ParseRule(const std::string & rule,const int ruleSize,const int number) const246 bool PluralFormatImpl::ParseRule(const std::string &rule, const int ruleSize, const int number) const
247 {
248     bool tempResult = true;
249     for (int i = 0; i < ruleSize; i++) {
250         bool curResult = ParseFormula(rule, ruleSize, i, number);
251         int nextSymbolIndex = i + SYMBOL_LENGTH;
252         if (curResult && tempResult) {
253             // If next symbol is or and current result and temp result are true, the final result should be true.
254             if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == OR)) {
255                 return true;
256             // If next symbol is and and current result and temp result are true, set the temp result to true and
257             // skip to next formula.
258             } else if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == AND)) {
259                 i += SKIP_SYMBOL_LENGTH;
260                 tempResult = true;
261             // If there is no symbol after this formula and current result and temp result are true,
262             // the final result should be true.
263             } else if (nextSymbolIndex >= ruleSize) {
264                 return true;
265             }
266         } else {
267             // If next symbol is or and current result or temp result is false, skip to next formula.
268             if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == OR)) {
269                 i += SKIP_SYMBOL_LENGTH;
270                 tempResult = true;
271             // If next symbol is and and current result or temp result is false, skip to next formula and
272             // set temp result to false.
273             } else if ((nextSymbolIndex < ruleSize) && (rule[nextSymbolIndex] == AND)) {
274                 i += SKIP_SYMBOL_LENGTH;
275                 tempResult = false;
276             } else if ((nextSymbolIndex >= ruleSize) && !ParseFormula(rule, ruleSize, i, number)) {
277                 tempResult = false;
278             }
279         }
280     }
281     return tempResult;
282 }
283 
ParseFormula(const std::string & rule,const int ruleSize,int & index,const int number) const284 bool PluralFormatImpl::ParseFormula(const std::string &rule, const int ruleSize, int &index, const int number) const
285 {
286     int currentNumber = number;
287     if ((index < ruleSize) && (rule[index] == MOD)) {
288         // Skip the module symbol and obtain the divisor number.
289         index += SKIP_SYMBOL_LENGTH;
290         int divisor = ParseNumber(rule, ruleSize, index);
291         if (divisor == 0) {
292             divisor = 1;
293         }
294         currentNumber = number % divisor;
295         index++;
296     }
297     // Compare the result with the equation
298     if (CompareResult(rule, ruleSize, index, currentNumber)) {
299         return true;
300     }
301     return false;
302 }
303 
CompareResult(const std::string & rule,const int ruleSize,int & index,const int number) const304 bool PluralFormatImpl::CompareResult(const std::string &rule, const int ruleSize, int &index, const int number) const
305 {
306     if (!((index < ruleSize) && (rule[index] == EQUAL))) {
307         return CompareNotEqualResult(rule, ruleSize, index, number);
308     }
309 
310     index += SKIP_SYMBOL_LENGTH;
311     int num = ParseNumber(rule, ruleSize, index);
312     bool temp = false;
313 
314     // Obtain all numbers in the formula
315     while ((index < ruleSize) && ((rule[index] == COMMA) || (rule[index] == TO))) {
316         if (rule[index] == TO) {
317             // If the symbol is "to", it indicates a number range.
318             int rangeStart = num;
319             index++;
320             int rangeEnd = ParseNumber(rule, ruleSize, index);
321             if ((number >= rangeStart) && (number <= rangeEnd)) {
322                 temp = true;
323             }
324         } else {
325             // Compare the input number with each number in the equation.
326             if (number == num) {
327                 temp = true;
328             }
329             index++;
330             num = ParseNumber(rule, ruleSize, index);
331         }
332     }
333     if (number == num) {
334         temp = true;
335     }
336     return temp;
337 }
338 
CompareNotEqualResult(const std::string & rule,const int ruleSize,int & index,const int number) const339 bool PluralFormatImpl::CompareNotEqualResult(const std::string &rule, const int ruleSize, int &index,
340     const int number) const
341 {
342     if (!((index < ruleSize) && (rule[index] == NOT_EQUAL))) {
343         return false;
344     }
345 
346     index += SKIP_SYMBOL_LENGTH;
347     int num = ParseNumber(rule, ruleSize, index);
348     bool temp = true;
349 
350     // Obtain all numbers in the formula
351     while ((index < ruleSize) && ((rule[index] == COMMA) || (rule[index] == TO))) {
352         if (rule[index] == TO) {
353             // If the symbol is "to", it indicates a number range.
354             index++;
355             int rangeStart = num;
356             int rangeEnd = ParseNumber(rule, ruleSize, index);
357             if ((number >= rangeStart) && (number <= rangeEnd)) {
358                 temp = false;
359             }
360         } else {
361             // Compare the input number with each number in the equation.
362             if (number == num) {
363                 temp = false;
364             }
365             index++;
366             num = ParseNumber(rule, ruleSize, index);
367         }
368     }
369     if (number == num) {
370         temp = false;
371     }
372     return temp;
373 }
374 
ParseNumber(const std::string & rule,const int ruleSize,int & index) const375 int PluralFormatImpl::ParseNumber(const std::string &rule, const int ruleSize, int &index) const
376 {
377     int num = 0;
378 
379     // Parse number in the formula.
380     while ((index < ruleSize) && (rule[index] != ' ') && (rule[index] != TO) && (rule[index] != COMMA)) {
381         num *= 10; // 10 means decimal. Calculate decimal value of the number.
382         num += rule[index] - '0';
383         index++;
384     }
385     return num;
386 }
387