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