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 double adjustNum = (num < 0) ? (-1 * num) : num;
133 int len = isPercentDefault ? static_cast<int>(sprintf_s(buff, NUMBER_MAX, "%.f", adjustNum)) :
134 static_cast<int>(sprintf_s(buff, NUMBER_MAX, defaultData->style.numFormat, adjustNum));
135 // convert decimal to char and format
136 if (len < 0) {
137 status = IERROR;
138 return "";
139 }
140 char *decimalNum = strchr(buff, NumberData::NUMBER_DECIMAL);
141 int decLen = (decimalNum == nullptr) ? 0 : strlen(decimalNum);
142 int lastLen = isShowGroup ? (len + CountGroupNum(len - decLen, defaultData->style.isTwoGroup)) : len;
143 char *result = reinterpret_cast<char *>(I18nMalloc(lastLen + 1));
144 if (result == nullptr) {
145 status = IERROR;
146 return "";
147 }
148 result[lastLen] = '\0';
149 bool adjustHasDec = isPercentDefault ? false : hasDec;
150 if (isShowGroup) {
151 char *resultAndContent[] = { result, buff };
152 int lengths[] = { lastLen, len, defaultData->style.isTwoGroup };
153 AddGroup(resultAndContent, lengths, decimalNum, adjustHasDec, decLen);
154 } else {
155 errno_t rc = strcpy_s(result, lastLen + 1, buff);
156 if (rc != EOK) {
157 I18nFree(static_cast<void *>(result));
158 return "";
159 }
160 }
161 // del more zero
162 lastLen = DelMoreZero(defaultData->style, decLen, lastLen, adjustHasDec, result);
163 // if percent
164 if (isPercent && !DealWithPercent(buff, result, status, defaultData->style, lastLen)) {
165 I18nFree(static_cast<void *>(result));
166 return "";
167 }
168 // if have native number to convert
169 std::string outStr = ConvertSignAndNum(result, lastLen, defaultData, defaultData->style);
170 I18nFree(static_cast<void *>(result));
171 if (num < 0) {
172 outStr.insert(0, defaultData->GetMinusSign());
173 }
174 return outStr;
175 }
176
DealWithPercent(char * buff,char * & result,int & status,StyleData & style,int & lastLen) const177 bool NumberFormatImpl::DealWithPercent(char *buff, char *&result, int &status, StyleData &style, int &lastLen) const
178 {
179 if (style.entireFormat != nullptr) {
180 bool cleanRet = CleanCharArray(buff, NUMBER_MAX);
181 if (!cleanRet) {
182 return false;
183 }
184 int len = static_cast<int>(sprintf_s(buff, NUMBER_MAX, style.entireFormat, result));
185 if (len < 0) {
186 status = IERROR;
187 I18nFree(static_cast<void *>(result));
188 return false;
189 }
190 char *perResult = reinterpret_cast<char *>(I18nMalloc(len + 1));
191 if (perResult == nullptr) {
192 return false;
193 }
194 errno_t rc = strcpy_s(perResult, len + 1, buff);
195 if (rc != EOK) {
196 I18nFree(static_cast<void *>(perResult));
197 return false;
198 }
199 perResult[len] = '\0';
200 lastLen = len;
201 I18nFree(static_cast<void *>(result));
202 result = perResult;
203 perResult = nullptr;
204 }
205 return true;
206 }
207
208
DelMoreZero(const StyleData & style,int decLen,int lastLen,bool hasDec,char * & result) const209 int NumberFormatImpl::DelMoreZero(const StyleData &style, int decLen, int lastLen, bool hasDec, char *&result) const
210 {
211 int num = 0;
212 if (decLen > 1) {
213 int delNum = decLen - 1;
214 num = DelZero(result, lastLen, delNum, true);
215 }
216 // delete more char
217 if ((maxDecimalLength != NO_SET) && (maxDecimalLength < decLen - 1 - num)) {
218 int delNum = decLen - 1 - num - maxDecimalLength;
219 num = num + DelZero(result, lastLen - num, delNum, false);
220 }
221 // fill zero to min
222 if (hasDec && (minDecimalLength != NO_SET) && (minDecimalLength > decLen - 1 - num)) {
223 if (decLen - 1 - num < 0) {
224 int add = minDecimalLength + 1;
225 char *tempResult = FillMinDecimal(result, lastLen - num, add, false);
226 if (result != nullptr) {
227 #ifdef I18N_PRODUCT
228 (void)OhosFree(static_cast<void *>(result));
229 #else
230 (void)free(result);
231 #endif
232 }
233 result = tempResult;
234 num = num - add;
235 } else {
236 int add = minDecimalLength - decLen + num + 1;
237 char *tempResult = FillMinDecimal(result, lastLen - num, add, true);
238 if (result != nullptr) {
239 #ifdef I18N_PRODUCT
240 (void)OhosFree(static_cast<void *>(result));
241 #else
242 (void)free(result);
243 #endif
244 }
245 result = tempResult;
246 num = num - add;
247 }
248 }
249 return lastLen - num;
250 }
251
CheckStatus(const errno_t rc,int & status) const252 void NumberFormatImpl::CheckStatus(const errno_t rc, int &status) const
253 {
254 if (rc != EOK) {
255 status = IERROR;
256 }
257 }
258
CountGroupNum(int intLength,bool isTwoGrouped) const259 int NumberFormatImpl::CountGroupNum(int intLength, bool isTwoGrouped) const
260 {
261 int groupNum;
262 if (!isTwoGrouped) {
263 groupNum = static_cast<int>(intLength / NumberData::NUMBER_GROUP);
264 if ((intLength % NumberData::NUMBER_GROUP) == 0) {
265 --groupNum;
266 }
267 } else {
268 if (intLength <= NumberData::NUMBER_GROUP) {
269 return 0;
270 }
271 intLength -= NumberData::NUMBER_GROUP;
272 groupNum = 1 + static_cast<int>(intLength / NumberData::TWO_GROUP);
273 if ((intLength % NumberData::TWO_GROUP) == 0) {
274 --groupNum;
275 }
276 }
277 return groupNum;
278 }
279
AddGroup(char * targetAndSource[],const int len[],const char * decimal,bool hasDec,int decLen) const280 void NumberFormatImpl::AddGroup(char *targetAndSource[], const int len[], const char *decimal,
281 bool hasDec, int decLen) const
282 {
283 // The len array must have at least 3 elements and the targetAndSource array
284 // must have at least 2 elements.
285 if ((targetAndSource == nullptr) || (len == nullptr)) {
286 return;
287 }
288 char *target = targetAndSource[0]; // use array to store target and source string, first is target string
289 int targetLen = len[0]; // use array to store target length and source length, first is target length
290 char *source = targetAndSource[1]; // use array to store target and source string, second is source string
291 int sourceLen = len[1]; // use array to store target length and source length, second is source length
292 int isTwoGroup = len[2]; // 2 is the index of group info
293 int intLen = sourceLen - decLen;
294 int addIndex = 0;
295 for (int i = 0; (i < intLen) && (addIndex < targetLen); i++, addIndex++) {
296 int index = intLen - i;
297 // ADD GROUP SIGN
298 if (isTwoGroup == 0) {
299 if ((index % NumberData::NUMBER_GROUP == 0) && (i != 0)) {
300 target[addIndex] = ',';
301 addIndex++;
302 }
303 target[addIndex] = source[i];
304 } else {
305 if ((index == NumberData::NUMBER_GROUP) && (i != 0)) {
306 target[addIndex] = ',';
307 addIndex++;
308 target[addIndex] = source[i];
309 } else if ((index > NumberData::NUMBER_GROUP) &&
310 ((index - NumberData::NUMBER_GROUP) % NumberData::TWO_GROUP == 0) && (i != 0)) {
311 target[addIndex] = ',';
312 addIndex++;
313 target[addIndex] = source[i];
314 } else {
315 target[addIndex] = source[i];
316 }
317 }
318 }
319 if (decLen > 0) {
320 target[addIndex] = hasDec ? '.' : '\0';
321 for (int j = 1; (j < decLen) && (addIndex < targetLen); j++) {
322 target[addIndex + j] = hasDec ? decimal[j] : '\0';
323 }
324 }
325 }
326
DelZero(char * target,int len,int delNum,bool onlyZero) const327 int NumberFormatImpl::DelZero(char *target, int len, int delNum, bool onlyZero) const
328 {
329 int num = 0;
330 for (int i = len - 1; (i > len - delNum - 1) && (i >= 0); i--) {
331 if ((target[i] != '0') && onlyZero) {
332 break;
333 }
334 target[i] = '\0';
335 num++;
336 if ((i - 1 > 0) && (target[i - 1] == '.')) {
337 target[i - 1] = '\0';
338 num++;
339 break;
340 }
341 }
342 return num;
343 }
344
Format(double num,NumberFormatType type,int & status) const345 std::string NumberFormatImpl::Format(double num, NumberFormatType type, int &status) const
346 {
347 if (defaultData == nullptr) {
348 status = IERROR;
349 return "";
350 }
351 if (type == PERCENT) { // percent,the decimal needs to be multiplied by 100.
352 return InnerFormat(num * 100, true, true, true, status);
353 } else {
354 return InnerFormat(num, true, true, false, status);
355 }
356 }
357
Format(int num,int & status) const358 std::string NumberFormatImpl::Format(int num, int &status) const
359 {
360 if (defaultData == nullptr) {
361 status = IERROR;
362 return "";
363 }
364 return InnerFormat(num, false, true, false, status);
365 }
366
FormatNoGroup(double num,NumberFormatType type,int & status) const367 std::string NumberFormatImpl::FormatNoGroup(double num, NumberFormatType type, int &status) const
368 {
369 if (defaultData == nullptr) {
370 status = IERROR;
371 return "";
372 }
373 if (type == PERCENT) { // percent,the decimal needs to be multiplied by 100.
374 return InnerFormat(num * 100, true, false, true, status);
375 } else {
376 return InnerFormat(num, true, false, false, status);
377 }
378 }
379
FormatNoGroup(int num,int & status) const380 std::string NumberFormatImpl::FormatNoGroup(int num, int &status) const
381 {
382 if (defaultData == nullptr) {
383 status = IERROR;
384 return "";
385 }
386 return InnerFormat(num, false, false, false, status);
387 }
388
SetMaxDecimalLength(int length)389 bool NumberFormatImpl::SetMaxDecimalLength(int length)
390 {
391 if (defaultData == nullptr) {
392 return false;
393 }
394 int adjustValue = (length < 0) ? -1 : length;
395 if ((minDecimalLength >= 0) && (minDecimalLength > adjustValue)) {
396 minDecimalLength = adjustValue;
397 defaultData->SetMinDecimalLength(adjustValue);
398 }
399 maxDecimalLength = adjustValue;
400 defaultData->SetMaxDecimalLength(length);
401 defaultData->UpdateNumberFormat();
402 return true;
403 }
404
SetMinDecimalLength(int length)405 bool NumberFormatImpl::SetMinDecimalLength(int length)
406 {
407 if (defaultData == nullptr) {
408 return false;
409 }
410 int adjustValue = (length < 0) ? -1 : length;
411 if ((maxDecimalLength >= 0) && (maxDecimalLength < adjustValue)) {
412 maxDecimalLength = adjustValue;
413 defaultData->SetMaxDecimalLength(maxDecimalLength);
414 }
415 minDecimalLength = adjustValue;
416 defaultData->SetMinDecimalLength(adjustValue);
417 defaultData->UpdateNumberFormat();
418 return true;
419 }
420
FillMinDecimal(const char * target,int len,int addSize,bool isDec) const421 char *NumberFormatImpl::FillMinDecimal(const char *target, int len, int addSize, bool isDec) const
422 {
423 char *content = I18nNewCharString(target, len + addSize);
424 if (content == nullptr) {
425 return nullptr;
426 }
427 for (int i = 0; i < addSize; i++) {
428 if ((!isDec) && (i == 0)) {
429 content[len + i] = '.';
430 continue;
431 }
432 content[len + i] = '0';
433 }
434 return content;
435 }
436