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