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 }