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 "number_format_impl.h"
17 #include "i18n_memory_adapter.h"
18 #include "number_data.h"
19 #include "str_util.h"
20
21 using namespace OHOS::I18N;
22
ConvertSignAndNum(const char * content,int len,NumberData * data,StyleData & style) const23 std::string NumberFormatImpl::ConvertSignAndNum(const char *content, int len, NumberData *data, StyleData &style) const
24 {
25 std::string strContent = content;
26 int off = 0;
27 for (int i = 0; i < len; i++) {
28 switch (content[i]) {
29 case NumberData::NUMBER_DECIMAL:
30 off = ReplaceAndCountOff(strContent, i + off, data->decimal, off);
31 break;
32 case NumberData::NUMBER_GROUPSIGN:
33 off = ReplaceAndCountOff(strContent, i + off, data->group, off);
34 break;
35 case NumberData::NUMBER_PERCENT:
36 off = ReplaceAndCountOff(strContent, i + off, data->percent, off);
37 break;
38 default:
39 break;
40 }
41 if (defaultData->isNative) {
42 off = ConvertNum(strContent, content[i], data, i, off);
43 }
44 }
45 return strContent;
46 }
47
ConvertNum(std::string & strContent,char currentChar,const NumberData * data,int index,int off) const48 int NumberFormatImpl::ConvertNum(std::string &strContent, char currentChar,
49 const NumberData *data, int index, int off) const
50 {
51 std::string numStr = "0123456789";
52 size_t charPos = numStr.find(currentChar);
53 return (charPos != std::string::npos) ?
54 ReplaceAndCountOff(strContent, index + off, data->nativeNums[charPos].c_str(), off) : off;
55 }
56
NumberFormatImpl(LocaleInfo & locale,int & status)57 NumberFormatImpl::NumberFormatImpl(LocaleInfo &locale, int &status)
58 {
59 if (locale.GetId() == nullptr) {
60 status = IERROR;
61 return;
62 }
63 mLocale = locale;
64 }
65
Init(const DataResource & resource)66 bool NumberFormatImpl::Init(const DataResource &resource)
67 {
68 std::string numebrSystemFormat;
69 std::string unprocessedNumberFormat = resource.GetString(DataResourceType::NUMBER_FORMAT);
70 std::string unprocessedNumberDigit = resource.GetString(DataResourceType::NUMBER_DIGIT);
71 std::string split[NUM_PATTERN_SIZE];
72 Split(unprocessedNumberFormat, split, NUM_PATTERN_SIZE, NUM_PATTERN_SEP);
73 std::string decSign = split[NUM_DEC_SIGN_INDEX];
74 std::string groupSign = split[NUM_GROUP_SIGN_INDEX];
75 std::string perSign = split[NUM_PERCENT_SIGN_INDEX];
76 const char *numberDigits = mLocale.GetExtension("nu");
77 if (numberDigits != nullptr) {
78 NumberData::GetNumberingSystem(numberDigits, numebrSystemFormat, unprocessedNumberDigit);
79 std::string temp[NUM_PATTERN_SIZE];
80 Split(numebrSystemFormat, temp, NUM_PATTERN_SIZE, NUM_PATTERN_SEP);
81 decSign = temp[NUM_DEC_SIGN_INDEX];
82 groupSign = temp[NUM_GROUP_SIGN_INDEX];
83 perSign = temp[NUM_PERCENT_SIGN_INDEX];
84 }
85 std::string origin = split[NUM_PERCENT_PAT_INDEX];
86 const char *pat = split[NUM_PAT_INDEX].c_str();
87 int size = origin.size();
88 std::string adjust = origin;
89 // strip "0x80 0xe2 0x8f" these three bytes in pat
90 if (size >= 3 && // check the last 3 character
91 (static_cast<unsigned char>(origin.at(size - 1)) == 0x8f) && // check whether the last one is 0x8f
92 (static_cast<unsigned char>(origin.at(size - 2)) == 0x80) && // check whether the index of size - 2 is 0x80
93 (static_cast<unsigned char>(origin.at(size - 3)) == 0xe2)) { // check whether the index of size - 3 is 0xe2
94 adjust = std::string(origin, 0, size - 3); // strip the last 3 chars
95 }
96 const char *percentPat = adjust.c_str();
97 defaultData = new(std::nothrow) NumberData(pat, percentPat, decSign, groupSign, perSign);
98 if (defaultData == nullptr) {
99 return false;
100 }
101 if (unprocessedNumberDigit != "") {
102 std::string splitDigit[NUM_DIGIT_SIZE];
103 Split(unprocessedNumberDigit, splitDigit, NUM_DIGIT_SIZE, NUM_DIGIT_SEP);
104 defaultData->SetNumSystem(splitDigit, NUM_DIGIT_SIZE);
105 }
106 // set minus sign
107 std::string minus = resource.GetString(DataResourceType::MINUS_SIGN);
108 defaultData->SetMinusSign(minus);
109 return true;
110 }
111
112
~NumberFormatImpl()113 NumberFormatImpl::~NumberFormatImpl()
114 {
115 if (defaultData != nullptr) {
116 delete defaultData;
117 defaultData = nullptr;
118 }
119 }
120
InnerFormat(double num,bool hasDec,bool isShowGroup,bool isPercent,int & status) const121 std::string NumberFormatImpl::InnerFormat(double num, bool hasDec, bool isShowGroup, bool isPercent,
122 int &status) const
123 {
124 if (defaultData == nullptr) {
125 return "";
126 }
127 char buff[NUMBER_MAX] = { 0 };
128 bool isPercentDefault = isPercent && (defaultData->style.minDecimalLength < 0);
129 int len;
130 double adjustNum = (num < 0) ? (-1 * num) : num;
131 if (isPercentDefault) {
132 len = static_cast<int>(sprintf_s(buff, NUMBER_MAX, "%.f", adjustNum));
133 } else {
134 len = static_cast<int>(sprintf_s(buff, NUMBER_MAX, defaultData->style.numFormat, adjustNum));
135 }
136 // convert decimal to char and format
137 if (len < 0) {
138 status = IERROR;
139 return "";
140 }
141 char *decimalNum = strchr(buff, NumberData::NUMBER_DECIMAL);
142 int decLen = (decimalNum == nullptr) ? 0 : strlen(decimalNum);
143 int adjustIntLength = len - decLen;
144 int lastLen = isShowGroup ? (len + CountGroupNum(adjustIntLength, defaultData->style.isTwoGroup)) : len;
145 char *result = reinterpret_cast<char *>(I18nMalloc(lastLen + 1));
146 if (result == nullptr) {
147 status = IERROR;
148 return "";
149 }
150 result[lastLen] = '\0';
151 bool adjustHasDec = isPercentDefault ? false : hasDec;
152 if (isShowGroup) {
153 char *resultAndContent[] = { result, buff };
154 int lengths[] = { lastLen, len, defaultData->style.isTwoGroup };
155 AddGroup(resultAndContent, lengths, decimalNum, adjustHasDec, decLen);
156 } else {
157 errno_t rc = strcpy_s(result, lastLen + 1, buff);
158 if (rc != EOK) {
159 I18nFree((void *)result);
160 return "";
161 }
162 }
163 // del more zero
164 lastLen = DelMoreZero(defaultData->style, decLen, lastLen, adjustHasDec, result);
165 // if percent
166 if (isPercent && !DealWithPercent(buff, result, status, defaultData->style, lastLen)) {
167 I18nFree((void *)result);
168 return "";
169 }
170 // if have native number to convert
171 std::string outStr = ConvertSignAndNum(result, lastLen, defaultData, defaultData->style);
172 I18nFree((void *)result);
173 if (num < 0) {
174 outStr.insert(0, defaultData->GetMinusSign());
175 }
176 return outStr;
177 }
178
DealWithPercent(char * buff,char * & result,int & status,StyleData & style,int & lastLen) const179 bool NumberFormatImpl::DealWithPercent(char *buff, char *&result, int &status, StyleData &style, int &lastLen) const
180 {
181 if (style.entireFormat != nullptr) {
182 bool cleanRet = CleanCharArray(buff, NUMBER_MAX);
183 if (!cleanRet) {
184 return false;
185 }
186 int len = static_cast<int>(sprintf_s(buff, NUMBER_MAX, style.entireFormat, result));
187 if (len < 0) {
188 status = IERROR;
189 I18nFree((void *)result);
190 return false;
191 }
192 char *perResult = reinterpret_cast<char *>(I18nMalloc(len + 1));
193 if (perResult == nullptr) {
194 return false;
195 }
196 errno_t rc = strcpy_s(perResult, len + 1, buff);
197 if (rc != EOK) {
198 I18nFree((void *)perResult);
199 return false;
200 }
201 perResult[len] = '\0';
202 lastLen = len;
203 I18nFree((void *)result);
204 result = perResult;
205 perResult = nullptr;
206 }
207 return true;
208 }
209
210
DelMoreZero(const StyleData & style,int decLen,int lastLen,bool hasDec,char * & result) const211 int NumberFormatImpl::DelMoreZero(const StyleData &style, int decLen, int lastLen, bool hasDec, char *&result) const
212 {
213 int num = 0;
214 if (decLen > 1) {
215 int delNum = decLen - 1;
216 num = DelZero(result, lastLen, delNum, true);
217 }
218 // delete more char
219 if ((maxDecimalLength != NO_SET) && (maxDecimalLength < decLen - 1 - num)) {
220 int delNum = decLen - 1 - num - maxDecimalLength;
221 num = num + DelZero(result, lastLen - num, delNum, false);
222 }
223 // fill zero to min
224 if (hasDec && (minDecimalLength != NO_SET) && (minDecimalLength > decLen - 1 - num)) {
225 if (decLen - 1 - num < 0) {
226 int add = minDecimalLength + 1;
227 char *tempResult = FillMinDecimal(result, lastLen - num, add, false);
228 if (result != nullptr) {
229 #ifdef I18N_PRODUCT
230 (void)OhosFree((void *) result);
231 #else
232 (void)free(result);
233 #endif
234 }
235 result = tempResult;
236 num = num - add;
237 } else {
238 int add = minDecimalLength - decLen + num + 1;
239 char *tempResult = FillMinDecimal(result, lastLen - num, add, true);
240 if (result != nullptr) {
241 #ifdef I18N_PRODUCT
242 (void)OhosFree((void *) result);
243 #else
244 (void)free(result);
245 #endif
246 }
247 result = tempResult;
248 num = num - add;
249 }
250 }
251 return lastLen - num;
252 }
253
CheckStatus(const errno_t rc,int & status) const254 void NumberFormatImpl::CheckStatus(const errno_t rc, int &status) const
255 {
256 if (rc != EOK) {
257 status = IERROR;
258 }
259 }
260
CountGroupNum(int intLength,bool isTwoGrouped) const261 int NumberFormatImpl::CountGroupNum(int intLength, bool isTwoGrouped) const
262 {
263 int groupNum;
264 if (!isTwoGrouped) {
265 groupNum = static_cast<int>(intLength / NumberData::NUMBER_GROUP);
266 if ((intLength % NumberData::NUMBER_GROUP) == 0) {
267 --groupNum;
268 }
269 } else {
270 if (intLength <= NumberData::NUMBER_GROUP) {
271 return 0;
272 }
273 intLength -= NumberData::NUMBER_GROUP;
274 groupNum = 1 + static_cast<int>(intLength / NumberData::TWO_GROUP);
275 if ((intLength % NumberData::TWO_GROUP) == 0) {
276 --groupNum;
277 }
278 }
279 return groupNum;
280 }
281
AddGroup(char * targetAndSource[],const int len[],const char * decimal,bool hasDec,int decLen) const282 void NumberFormatImpl::AddGroup(char *targetAndSource[], const int len[], const char *decimal,
283 bool hasDec, int decLen) const
284 {
285 // The len array must have at least 3 elements and the targetAndSource array
286 // must have at least 2 elements.
287 if ((targetAndSource == nullptr) || (len == nullptr)) {
288 return;
289 }
290 char *target = targetAndSource[0]; // use array to store target and source string, first is target string
291 int targetLen = len[0]; // use array to store target length and source length, first is target length
292 char *source = targetAndSource[1]; // use array to store target and source string, second is source string
293 int sourceLen = len[1]; // use array to store target length and source length, second is source length
294 int isTwoGroup = len[2]; // 2 is the index of group info
295 int intLen = sourceLen - decLen;
296 int addIndex = 0;
297 for (int i = 0; (i < intLen) && (addIndex < targetLen); i++, addIndex++) {
298 int index = intLen - i;
299 // ADD GROUP SIGN
300 if (isTwoGroup == 0) {
301 if ((index % NumberData::NUMBER_GROUP == 0) && (i != 0)) {
302 target[addIndex] = ',';
303 addIndex++;
304 }
305 target[addIndex] = source[i];
306 } else {
307 if ((index == NumberData::NUMBER_GROUP) && (i != 0)) {
308 target[addIndex] = ',';
309 addIndex++;
310 target[addIndex] = source[i];
311 } else if ((index > NumberData::NUMBER_GROUP) &&
312 ((index - NumberData::NUMBER_GROUP) % NumberData::TWO_GROUP == 0) && (i != 0)) {
313 target[addIndex] = ',';
314 addIndex++;
315 target[addIndex] = source[i];
316 } else {
317 target[addIndex] = source[i];
318 }
319 }
320 }
321 if (decLen > 0) {
322 target[addIndex] = hasDec ? '.' : '\0';
323 for (int j = 1; (j < decLen) && (addIndex < targetLen); j++) {
324 target[addIndex + j] = hasDec ? decimal[j] : '\0';
325 }
326 }
327 }
328
DelZero(char * target,int len,int delNum,bool onlyZero) const329 int NumberFormatImpl::DelZero(char *target, int len, int delNum, bool onlyZero) const
330 {
331 int num = 0;
332 for (int i = len - 1; (i > len - delNum - 1) && (i >= 0); i--) {
333 if ((target[i] != '0') && onlyZero) {
334 break;
335 }
336 target[i] = '\0';
337 num++;
338 if ((i - 1 > 0) && (target[i - 1] == '.')) {
339 target[i - 1] = '\0';
340 num++;
341 break;
342 }
343 }
344 return num;
345 }
346
Format(double num,NumberFormatType type,int & status) const347 std::string NumberFormatImpl::Format(double num, NumberFormatType type, int &status) const
348 {
349 if (defaultData == nullptr) {
350 status = IERROR;
351 return "";
352 }
353 if (type == PERCENT) { // percent,the decimal needs to be multiplied by 100.
354 return InnerFormat(num * 100, true, true, true, status);
355 } else {
356 return InnerFormat(num, true, true, false, status);
357 }
358 }
359
Format(int num,int & status) const360 std::string NumberFormatImpl::Format(int num, int &status) const
361 {
362 if (defaultData == nullptr) {
363 status = IERROR;
364 return "";
365 }
366 return InnerFormat(num, false, true, false, status);
367 }
368
FormatNoGroup(double num,NumberFormatType type,int & status) const369 std::string NumberFormatImpl::FormatNoGroup(double num, NumberFormatType type, int &status) const
370 {
371 if (defaultData == nullptr) {
372 status = IERROR;
373 return "";
374 }
375 if (type == PERCENT) { // percent,the decimal needs to be multiplied by 100.
376 return InnerFormat(num * 100, true, false, true, status);
377 } else {
378 return InnerFormat(num, true, false, false, status);
379 }
380 }
381
FormatNoGroup(int num,int & status) const382 std::string NumberFormatImpl::FormatNoGroup(int num, int &status) const
383 {
384 if (defaultData == nullptr) {
385 status = IERROR;
386 return "";
387 }
388 return InnerFormat(num, false, false, false, status);
389 }
390
SetMaxDecimalLength(int length)391 bool NumberFormatImpl::SetMaxDecimalLength(int length)
392 {
393 if (defaultData == nullptr) {
394 return false;
395 }
396 int adjustValue = (length < 0) ? -1 : length;
397 if ((minDecimalLength >= 0) && (minDecimalLength > adjustValue)) {
398 minDecimalLength = adjustValue;
399 defaultData->SetMinDecimalLength(adjustValue);
400 }
401 maxDecimalLength = adjustValue;
402 defaultData->SetMaxDecimalLength(length);
403 defaultData->UpdateNumberFormat();
404 return true;
405 }
406
SetMinDecimalLength(int length)407 bool NumberFormatImpl::SetMinDecimalLength(int length)
408 {
409 if (defaultData == nullptr) {
410 return false;
411 }
412 int adjustValue = (length < 0) ? -1 : length;
413 if ((maxDecimalLength >= 0) && (maxDecimalLength < adjustValue)) {
414 maxDecimalLength = adjustValue;
415 defaultData->SetMaxDecimalLength(maxDecimalLength);
416 }
417 minDecimalLength = adjustValue;
418 defaultData->SetMinDecimalLength(adjustValue);
419 defaultData->UpdateNumberFormat();
420 return true;
421 }
422
FillMinDecimal(const char * target,int len,int addSize,bool isDec) const423 char *NumberFormatImpl::FillMinDecimal(const char *target, int len, int addSize, bool isDec) const
424 {
425 char *content = I18nNewCharString(target, len + addSize);
426 if (content == nullptr) {
427 return nullptr;
428 }
429 for (int i = 0; i < addSize; i++) {
430 if ((!isDec) && (i == 0)) {
431 content[len + i] = '.';
432 continue;
433 }
434 content[len + i] = '0';
435 }
436 return content;
437 }
438