1 /*
2 * Copyright (c) 2021 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 "utils/string_utils.h"
17
18 #include <cctype>
19 #include <cstdarg>
20 #include <cstdint>
21 #include <limits>
22 #include <vector>
23 #include <regex>
24 #include <algorithm>
25 #include "hilog_wrapper.h"
26
27 #ifdef SUPPORT_GRAPHICS
28 #include "unicode/numberformatter.h"
29 #endif
30
31 #if defined(__WINNT__)
32 #include <cstring>
33 #else
34 #include "securec.h"
35 #endif
36
37 namespace OHOS {
38 namespace Global {
39 namespace Resource {
40 const std::regex PLACEHOLDER_MATCHING_RULES(R"((%%)|%((\d+)\$){0,1}(\.\d)?([dsf]))");
41 // the whole string match the regex, such as "%1$.2f" in the string "count is %1$.2f"
42 constexpr uint8_t MATCHE_INDEX_WHOLE_STRING = 0;
43 // match %%
44 constexpr uint8_t MATCHE_INDEX_DOUBLE_PERCENT = 1;
45 // match the placeholder index number, such as 1 in 1$
46 constexpr uint8_t MATCHE_INDEX_PLACEHOLDER_INDEX = 3;
47 // match precision, such as .2f
48 constexpr uint8_t MATCHE_INDEX_PRECISION = 4;
49 // match type [dsf]
50 constexpr uint8_t MATCHE_INDEX_PLACEHOLDER_TYPE = 5;
51 constexpr int32_t INVALID_PRECISION = -1;
52 const std::string SIZE_T_MAX_STR = std::to_string(std::numeric_limits<size_t>::max());
53 #ifdef SUPPORT_GRAPHICS
54 #endif
55
FormatString(const char *fmt, ...)56 std::string FormatString(const char *fmt, ...)
57 {
58 std::string strResult;
59 if (fmt != nullptr) {
60 va_list marker;
61 va_start(marker, fmt);
62 strResult = FormatString(fmt, marker);
63 va_end(marker);
64 }
65 return strResult;
66 }
67
FormatString(const char *fmt, va_list args)68 std::string FormatString(const char *fmt, va_list args)
69 {
70 std::string strResult;
71 if (fmt != nullptr) {
72 va_list tmpArgs;
73 va_copy(tmpArgs, args);
74 int nLength = vsnprintf(nullptr, 0, fmt, tmpArgs); // compute buffer size
75 va_end(tmpArgs);
76 std::vector<char> vBuffer(nLength + 1, '\0');
77 int nWritten = vsnprintf_s(&vBuffer[0], nLength + 1, nLength, fmt, args);
78 if (nWritten > 0) {
79 strResult = &vBuffer[0];
80 }
81 }
82 return strResult;
83 }
84
getJsParams(const std::string &inputOutputValue, va_list args, std::vector<std::pair<int, std::string>> paramsWithOutNum, std::vector<std::pair<int, std::string>> paramsWithNum, std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)85 bool getJsParams(const std::string &inputOutputValue, va_list args,
86 std::vector<std::pair<int, std::string>> paramsWithOutNum,
87 std::vector<std::pair<int, std::string>> paramsWithNum,
88 std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)
89 {
90 std::sort(paramsWithNum.begin(), paramsWithNum.end(),
91 [](const std::pair<int, std::string>& a, const std::pair<int, std::string>& b) {
92 return a.first < b.first;
93 });
94 for (size_t i = 0; i < paramsWithOutNum.size(); i++) {
95 std::string type = paramsWithOutNum[i].second;
96 if (type == "d") {
97 int temp = va_arg(args, int);
98 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
99 } else if (type == "s") {
100 char *temp = va_arg(args, char*);
101 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_STRING, temp);
102 } else if (type == "f") {
103 float temp = va_arg(args, double);
104 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
105 }
106 }
107 for (size_t i = 0; i < paramsWithNum.size(); i++) {
108 size_t index = static_cast<size_t>(paramsWithNum[i].first);
109 std::string type = paramsWithNum[i].second;
110 if (index < paramsWithOutNum.size()) {
111 if (type != paramsWithOutNum[index].second) {
112 return false;
113 }
114 } else if (index == paramsWithOutNum.size()) {
115 paramsWithOutNum.push_back({index, type});
116 if (type == "d") {
117 int temp = va_arg(args, int);
118 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
119 } else if (type == "s") {
120 char *temp = va_arg(args, char*);
121 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_STRING, temp);
122 } else if (type == "f") {
123 float temp = va_arg(args, double);
124 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
125 }
126 } else {
127 return false;
128 }
129 }
130 return true;
131 }
132
parseArgs(const std::string &inputOutputValue, va_list args, std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)133 bool parseArgs(const std::string &inputOutputValue, va_list args,
134 std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)
135 {
136 if (inputOutputValue.empty()) {
137 return true;
138 }
139 std::string::const_iterator start = inputOutputValue.begin();
140 std::string::const_iterator end = inputOutputValue.end();
141 std::smatch matches;
142 size_t matchCount = 0;
143 int prefixLength = 0;
144 int offset = 2;
145 std::vector<std::pair<int, std::string>> paramsWithOutNum;
146 std::vector<std::pair<int, std::string>> paramsWithNum;
147 while (std::regex_search(start, end, matches, PLACEHOLDER_MATCHING_RULES)) {
148 prefixLength = matches[MATCHE_INDEX_WHOLE_STRING].first - inputOutputValue.begin();
149 if (matches[1].length() != 0) {
150 start = inputOutputValue.begin() + prefixLength + offset;
151 continue;
152 }
153 std::string placeholderIndex = matches[MATCHE_INDEX_PLACEHOLDER_INDEX];
154 std::string placeholderType = matches[MATCHE_INDEX_PLACEHOLDER_TYPE];
155 size_t paramIndex;
156 if (placeholderIndex.length() != 0) {
157 if (placeholderIndex.size() > SIZE_T_MAX_STR.size() ||
158 (placeholderIndex.size() == SIZE_T_MAX_STR.size() && placeholderIndex > SIZE_T_MAX_STR)) {
159 RESMGR_HILOGE(RESMGR_TAG, "index of placeholder is too large");
160 return false;
161 }
162 if (std::stoul(placeholderIndex) == 0) {
163 return false;
164 }
165 paramIndex = std::stoul(placeholderIndex) - 1;
166 paramsWithNum.push_back({paramIndex, placeholderType});
167 } else {
168 paramIndex = matchCount++;
169 paramsWithOutNum.push_back({paramIndex, placeholderType});
170 }
171 start = inputOutputValue.begin() + prefixLength + matches[0].length();
172 }
173 return getJsParams(inputOutputValue, args, paramsWithOutNum, paramsWithNum, jsParams);
174 }
175
LocalizeNumber(std::string &inputOutputNum, const ResConfigImpl &resConfig, const int32_t precision = INVALID_PRECISION)176 bool LocalizeNumber(std::string &inputOutputNum, const ResConfigImpl &resConfig,
177 const int32_t precision = INVALID_PRECISION)
178 {
179 #ifdef SUPPORT_GRAPHICS
180 const ResLocale *resLocale = resConfig.GetResLocale();
181 if (resLocale == nullptr) {
182 RESMGR_HILOGW(RESMGR_TAG, "LocalizeNumber resLocale is null");
183 return true;
184 }
185
186 std::string localeInfo;
187 const char *language = resLocale->GetLanguage();
188 if (language != nullptr && strlen(language) > 0) {
189 localeInfo.assign(language);
190 } else {
191 RESMGR_HILOGW(RESMGR_TAG, "LocalizeNumber language is null");
192 return true;
193 }
194 std::string temp;
195 const char *script = resLocale->GetScript();
196 if (script != nullptr && strlen(script) > 0) {
197 temp.assign(script);
198 localeInfo += "-" + temp;
199 }
200 const char *region = resLocale->GetRegion();
201 if (region != nullptr && strlen(region) > 0) {
202 temp.assign(region);
203 localeInfo += "-" + temp;
204 }
205
206 icu::Locale locale(localeInfo.c_str());
207 if (locale.isBogus()) {
208 return true;
209 }
210
211 icu::number::LocalizedNumberFormatter numberFormat = icu::number::NumberFormatter::withLocale(locale);
212 numberFormat = numberFormat.grouping(UNumberGroupingStrategy::UNUM_GROUPING_OFF);
213 numberFormat = numberFormat.roundingMode(UNUM_ROUND_HALFUP);
214 if (precision != INVALID_PRECISION) {
215 numberFormat = numberFormat.precision(icu::number::Precision::fixedFraction(precision));
216 }
217 UErrorCode status = U_ZERO_ERROR;
218 double num = std::stod(inputOutputNum);
219 inputOutputNum.clear();
220 numberFormat.formatDouble(num, status).toString(status).toUTF8String(inputOutputNum);
221 if (status == U_ZERO_ERROR) {
222 RESMGR_HILOGE(RESMGR_TAG, "LocalizeNumber failed, status = %{public}d", status);
223 return false;
224 }
225 return true;
226 #else
227 return true;
228 #endif
229 }
230
GetReplaceStr(const std::tuple<ResourceManager::NapiValueType, std::string> &jsParam, const std::string &placeHolderType, int32_t precision, const ResConfigImpl &config, std::string &replaceStr)231 bool GetReplaceStr(const std::tuple<ResourceManager::NapiValueType, std::string> &jsParam,
232 const std::string &placeHolderType, int32_t precision, const ResConfigImpl &config, std::string &replaceStr)
233 {
234 ResourceManager::NapiValueType paramType = std::get<0>(jsParam);
235 std::string paramValue = std::get<1>(jsParam);
236
237 // string type
238 if (placeHolderType == "s") {
239 if (paramType != ResourceManager::NapiValueType::NAPI_STRING) {
240 RESMGR_HILOGE(RESMGR_TAG, "the type of placeholder and param does not match");
241 return false;
242 }
243 replaceStr = paramValue;
244 return true;
245 }
246
247 // number type
248 if (paramType != ResourceManager::NapiValueType::NAPI_NUMBER) {
249 RESMGR_HILOGE(RESMGR_TAG, "the type of placeholder and param does not match");
250 return false;
251 }
252
253 // int type
254 if (placeHolderType == "d") {
255 size_t posOfDecimalPoint = paramValue.find(".");
256 replaceStr = paramValue.substr(0, posOfDecimalPoint);
257 return LocalizeNumber(replaceStr, config);
258 }
259
260 // double type
261 replaceStr = paramValue;
262 return LocalizeNumber(replaceStr, config, precision);
263 }
264
MatchPlaceholderIndex(std::string placeholderIndex, size_t ¶mIndex, size_t &matchCount)265 bool MatchPlaceholderIndex(std::string placeholderIndex, size_t ¶mIndex, size_t &matchCount)
266 {
267 if (placeholderIndex.length() != 0) {
268 if (placeholderIndex.size() > SIZE_T_MAX_STR.size() ||
269 (placeholderIndex.size() == SIZE_T_MAX_STR.size() && placeholderIndex > SIZE_T_MAX_STR)) {
270 RESMGR_HILOGE(RESMGR_TAG, "index of placeholder is too large");
271 return false;
272 }
273 if (std::stoul(placeholderIndex) < 1) {
274 return false;
275 }
276 paramIndex = std::stoul(placeholderIndex) - 1;
277 } else {
278 paramIndex = matchCount++;
279 }
280 return true;
281 }
282
GetPrecision(const std::string &precisionStr)283 int32_t GetPrecision(const std::string &precisionStr)
284 {
285 size_t posOfDecimalPoint = precisionStr.find(".");
286 if (posOfDecimalPoint != std::string::npos) {
287 return std::stoi(precisionStr.substr(posOfDecimalPoint + 1));
288 }
289 return INVALID_PRECISION;
290 }
291
ReplacePlaceholderWithParams(std::string &inputOutputValue, const ResConfigImpl &resConfig, const std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)292 bool ReplacePlaceholderWithParams(std::string &inputOutputValue, const ResConfigImpl &resConfig,
293 const std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)
294 {
295 if (inputOutputValue.empty()) {
296 return true;
297 }
298
299 std::string::const_iterator start = inputOutputValue.begin();
300 std::string::const_iterator end = inputOutputValue.end();
301 std::smatch matches;
302 size_t matchCount = 0;
303 int prefixLength = 0;
304
305 while (std::regex_search(start, end, matches, PLACEHOLDER_MATCHING_RULES)) {
306 prefixLength = matches[MATCHE_INDEX_WHOLE_STRING].first - inputOutputValue.begin();
307 // Matched to %%, replace it with %
308 if (matches[MATCHE_INDEX_DOUBLE_PERCENT].length() != 0) {
309 inputOutputValue.erase(matches[MATCHE_INDEX_DOUBLE_PERCENT].first);
310 start = inputOutputValue.begin() + prefixLength + 1;
311 end = inputOutputValue.end();
312 continue;
313 } else if (jsParams.size() == 0) { // Matched to placeholder but no params, ignore placehold
314 start = inputOutputValue.begin() + prefixLength + matches[0].length();
315 continue;
316 }
317
318 // Matched to placeholder, check and parse param index
319 std::string placeholderIndex = matches[MATCHE_INDEX_PLACEHOLDER_INDEX];
320 size_t paramIndex;
321 if (!MatchPlaceholderIndex(placeholderIndex, paramIndex, matchCount)) {
322 return false;
323 }
324 if (paramIndex >= jsParams.size()) {
325 RESMGR_HILOGE(RESMGR_TAG, "index of placeholder out of range");
326 return false;
327 }
328 // Replace placeholder with corresponding param
329 std::string replaceStr;
330 int32_t precision = GetPrecision(matches[MATCHE_INDEX_PRECISION]);
331 std::string placeholderType = matches[MATCHE_INDEX_PLACEHOLDER_TYPE];
332 if (!GetReplaceStr(jsParams[paramIndex], placeholderType, precision, resConfig, replaceStr)) {
333 return false;
334 }
335 inputOutputValue.replace(prefixLength, matches[MATCHE_INDEX_WHOLE_STRING].length(), replaceStr);
336
337 // Update iterator
338 start = inputOutputValue.begin() + prefixLength + replaceStr.length();
339 end = inputOutputValue.end();
340 }
341 return true;
342 }
343 } // namespace Resource
344 } // namespace Global
345 } // namespace OHOS