• 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 = resource.GetString(DataResourceType::PLURAL);
46     std::string unprocessedDecimalPluralData = resource.GetString(DataResourceType::DECIMAL_PLURAL);
47     mPluralRules = InitPluralRules(unprocessedPluralData);
48     mDecimalPluralRules = InitPluralRules(unprocessedDecimalPluralData);
49     if (mPluralRules == nullptr || mDecimalPluralRules == nullptr) {
50         return false;
51     }
52     return true;
53 }
54 
InitPluralRules(std::string unprocessedPluralData)55 PluralRules *PluralFormatImpl::InitPluralRules(std::string unprocessedPluralData)
56 {
57     std::string rules[RULES_NUM];
58     Split(unprocessedPluralData, rules, RULES_NUM, PLURAL_SEP);
59     int zeroRuleSize = static_cast<int>(rules[PluralRuleType::ZERO].size());
60     int oneRuleSize = static_cast<int>(rules[PluralRuleType::ONE].size());
61     int twoRuleSize = static_cast<int>(rules[PluralRuleType::TWO].size());
62     int fewRuleSize = static_cast<int>(rules[PluralRuleType::FEW].size());
63     int manyRuleSize = static_cast<int>(rules[PluralRuleType::MANY].size());
64     int otherRuleSize = static_cast<int>(rules[PluralRuleType::OTHER].size());
65     int ruleSizes[RULES_NUM] = { zeroRuleSize, oneRuleSize, twoRuleSize, fewRuleSize, manyRuleSize, otherRuleSize };
66     return new PluralRules(rules, RULES_NUM, ruleSizes, RULES_NUM);
67 }
68 
GetPluralData(I18nStatus status) const69 PluralRules *PluralFormatImpl::GetPluralData(I18nStatus status) const
70 {
71     if (status == IERROR) {
72         return nullptr;
73     }
74     return mPluralRules;
75 }
76 
GetPluralRuleIndex(double number,I18nStatus status) const77 int PluralFormatImpl::GetPluralRuleIndex(double number, I18nStatus status) const
78 {
79     if (status == IERROR) {
80         return -1;
81     }
82     if (GetPluralData(status) == nullptr) {
83         return PluralRuleType::OTHER;
84     }
85     int integerNumber = (int)number;
86     int numberInfo[NUMBER_INFO_SIZE];
87     ComputeDecimalInfo(number, integerNumber, numberInfo, NUMBER_INFO_SIZE);
88     if ((mDecimalPluralRules->mZeroRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mZeroRule,
89         mDecimalPluralRules->mZeroRuleSize, numberInfo, NUMBER_INFO_SIZE)) {
90         return PluralRuleType::ZERO;
91     } else if ((mDecimalPluralRules->mOneRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mOneRule,
92         mDecimalPluralRules->mOneRuleSize, numberInfo, NUMBER_INFO_SIZE)) {
93         return PluralRuleType::ONE;
94     } else if ((mDecimalPluralRules->mTwoRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mTwoRule,
95         mDecimalPluralRules->mTwoRuleSize, numberInfo, NUMBER_INFO_SIZE)) {
96         return PluralRuleType::TWO;
97     } else if ((mDecimalPluralRules->mFewRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mFewRule,
98         mDecimalPluralRules->mFewRuleSize, numberInfo, NUMBER_INFO_SIZE)) {
99         return PluralRuleType::FEW;
100     } else if ((mDecimalPluralRules->mManyRuleSize > 0) && ParseDecimalRule(mDecimalPluralRules->mManyRule,
101         mDecimalPluralRules->mManyRuleSize, numberInfo, NUMBER_INFO_SIZE)) {
102         return PluralRuleType::MANY;
103     } else {
104         if (!CheckContainsIntegerRule() && numberInfo[FRACTION_NUMBER_INDEX] == 0) {
105             return GetPluralRuleIndex(integerNumber, status);
106         }
107         return PluralRuleType::OTHER;
108     }
109 }
110 
CheckContainsIntegerRule() const111 bool PluralFormatImpl::CheckContainsIntegerRule() const
112 {
113     if ((mDecimalPluralRules->mZeroRuleSize > 0) &&
114         (mDecimalPluralRules->mZeroRule.find("v = 0") != std::string::npos)) {
115         return true;
116     } else if ((mDecimalPluralRules->mOneRuleSize > 0) &&
117         (mDecimalPluralRules->mOneRule.find("v = 0") != std::string::npos)) {
118         return true;
119     } else if ((mDecimalPluralRules->mTwoRuleSize > 0) &&
120         (mDecimalPluralRules->mTwoRule.find("v = 0") != std::string::npos)) {
121         return true;
122     } else if ((mDecimalPluralRules->mFewRuleSize > 0) &&
123         (mDecimalPluralRules->mFewRule.find("v = 0") != std::string::npos)) {
124         return true;
125     } else if ((mDecimalPluralRules->mManyRuleSize > 0) &&
126         (mDecimalPluralRules->mManyRule.find("v = 0") != std::string::npos)) {
127         return true;
128     } else {
129         return false;
130     }
131 }
132 
ComputeDecimalInfo(double number,int integerNumber,int * numberInfo,const int numberInfoSize) const133 void PluralFormatImpl::ComputeDecimalInfo(double number, int integerNumber, int *numberInfo,
134     const int numberInfoSize) const
135 {
136     int fractionNumber = 0;
137     int numOfFraction = 0;
138     for (int i = 1; i <= MAX_FRACTION_NUMBERS; i++) {
139         // Calculate number of fraction digits in the decimal number by judging whether
140         // multiplying by 10 is an integer.
141         double temp = number * pow(10, i);
142         if (i == MAX_FRACTION_NUMBERS && temp - ((int)temp) >= EPS) {
143             temp = round(temp);
144         }
145         if (temp - ((int)temp) < EPS) {
146             while (i > 1 && (int) temp % 10 == 0) {
147                 i--;
148                 temp /= 10;
149             }
150             int tempInteger = integerNumber * pow(10, i);
151             fractionNumber = temp - tempInteger;
152             numOfFraction = i;
153             break;
154         }
155     }
156     numberInfo[INTEGER_NUMBER_INDEX] = integerNumber;
157     numberInfo[FRACTION_NUMBER_INDEX] = fractionNumber;
158     numberInfo[NUM_OF_FRACTION_INDEX] = numOfFraction;
159 }
160 
ParseDecimalRule(const std::string & rule,const int ruleSize,const int * numberInfo,const int numberInfoSize) const161 bool PluralFormatImpl::ParseDecimalRule(const std::string &rule, const int ruleSize, const int *numberInfo,
162     const int numberInfoSize) const
163 {
164     bool tempResult = true;
165     for (int i = 0; i < ruleSize; i++) {
166         bool curResult = ParseDecimalFormula(rule, ruleSize, i, numberInfo, numberInfoSize);
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, numberInfoSize)) {
194                 tempResult = false;
195             }
196         }
197     }
198     return tempResult;
199 }
200 
ParseDecimalFormula(const std::string & rule,const int ruleSize,int & index,const int * numberInfo,const int numberInfoSize) const201 bool PluralFormatImpl::ParseDecimalFormula(const std::string &rule, const int ruleSize, int &index,
202     const int *numberInfo, const int numberInfoSize) 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 *= 10; // Calculate decimal value of the number.
380         num += rule[index] - '0';
381         index++;
382     }
383     return num;
384 }