• 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);
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