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 #include "utils/utils.h"
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 unsigned long index;
163 if (!Utils::convertToUnsignedLong(placeholderIndex, index) || index < 1) {
164 RESMGR_HILOGE(RESMGR_TAG, "convert value error, placeholderIndex = %{public}s",
165 placeholderIndex.c_str());
166 return false;
167 }
168 paramIndex = index - 1;
169 paramsWithNum.push_back({paramIndex, placeholderType});
170 } else {
171 paramIndex = matchCount++;
172 paramsWithOutNum.push_back({paramIndex, placeholderType});
173 }
174 start = inputOutputValue.begin() + prefixLength + matches[0].length();
175 }
176 return getJsParams(inputOutputValue, args, paramsWithOutNum, paramsWithNum, jsParams);
177 }
178
GetLocalInfo(const ResLocale * resLocale)179 std::string GetLocalInfo(const ResLocale *resLocale)
180 {
181 std::string localeInfo;
182 const char *language = resLocale->GetLanguage();
183 if (language != nullptr && strlen(language) > 0) {
184 localeInfo.assign(language);
185 } else {
186 RESMGR_HILOGW(RESMGR_TAG, "GetLocalInfo language is null");
187 return localeInfo;
188 }
189 std::string temp;
190 const char *script = resLocale->GetScript();
191 if (script != nullptr && strlen(script) > 0) {
192 temp.assign(script);
193 localeInfo += "-" + temp;
194 }
195 const char *region = resLocale->GetRegion();
196 if (region != nullptr && strlen(region) > 0) {
197 temp.assign(region);
198 localeInfo += "-" + temp;
199 }
200 return localeInfo;
201 }
202
LocalizeNumber(std::string & inputOutputNum,const ResConfigImpl & resConfig,const int32_t precision=INVALID_PRECISION)203 bool LocalizeNumber(std::string &inputOutputNum, const ResConfigImpl &resConfig,
204 const int32_t precision = INVALID_PRECISION)
205 {
206 #ifdef SUPPORT_GRAPHICS
207 const ResLocale *resLocale = resConfig.GetResLocale();
208 if (resLocale == nullptr) {
209 RESMGR_HILOGW(RESMGR_TAG, "LocalizeNumber resLocale is null");
210 return true;
211 }
212
213 std::string localeInfo = GetLocalInfo(resLocale);
214 if (localeInfo.empty()) {
215 return true;
216 }
217 icu::Locale locale(localeInfo.c_str());
218 if (locale.isBogus()) {
219 return true;
220 }
221
222 icu::number::LocalizedNumberFormatter numberFormat = icu::number::NumberFormatter::withLocale(locale);
223 numberFormat = numberFormat.grouping(UNumberGroupingStrategy::UNUM_GROUPING_OFF);
224 numberFormat = numberFormat.roundingMode(UNUM_ROUND_HALFUP);
225 if (precision != INVALID_PRECISION) {
226 numberFormat = numberFormat.precision(icu::number::Precision::fixedFraction(precision));
227 }
228 double num;
229 if (!Utils::convertToDouble(inputOutputNum, num)) {
230 return false;
231 }
232 inputOutputNum.clear();
233 UErrorCode status = U_ZERO_ERROR;
234 numberFormat.formatDouble(num, status).toString(status).toUTF8String(inputOutputNum);
235 if (status == U_ZERO_ERROR) {
236 RESMGR_HILOGE(RESMGR_TAG, "LocalizeNumber failed, status = %{public}d", status);
237 return false;
238 }
239 return true;
240 #else
241 return true;
242 #endif
243 }
244
GetReplaceStr(const std::tuple<ResourceManager::NapiValueType,std::string> & jsParam,const std::string & placeHolderType,int32_t precision,const ResConfigImpl & config,std::string & replaceStr)245 bool GetReplaceStr(const std::tuple<ResourceManager::NapiValueType, std::string> &jsParam,
246 const std::string &placeHolderType, int32_t precision, const ResConfigImpl &config, std::string &replaceStr)
247 {
248 ResourceManager::NapiValueType paramType = std::get<0>(jsParam);
249 std::string paramValue = std::get<1>(jsParam);
250
251 // string type
252 if (placeHolderType == "s") {
253 if (paramType != ResourceManager::NapiValueType::NAPI_STRING) {
254 RESMGR_HILOGE(RESMGR_TAG, "the type of placeholder and param does not match");
255 return false;
256 }
257 replaceStr = paramValue;
258 return true;
259 }
260
261 // number type
262 if (paramType != ResourceManager::NapiValueType::NAPI_NUMBER) {
263 RESMGR_HILOGE(RESMGR_TAG, "the type of placeholder and param does not match");
264 return false;
265 }
266
267 // int type
268 if (placeHolderType == "d") {
269 size_t posOfDecimalPoint = paramValue.find(".");
270 replaceStr = paramValue.substr(0, posOfDecimalPoint);
271 return LocalizeNumber(replaceStr, config);
272 }
273
274 // double type
275 replaceStr = paramValue;
276 return LocalizeNumber(replaceStr, config, precision);
277 }
278
MatchPlaceholderIndex(std::string placeholderIndex,size_t & paramIndex,size_t & matchCount)279 bool MatchPlaceholderIndex(std::string placeholderIndex, size_t ¶mIndex, size_t &matchCount)
280 {
281 if (placeholderIndex.length() != 0) {
282 if (placeholderIndex.size() > SIZE_T_MAX_STR.size() ||
283 (placeholderIndex.size() == SIZE_T_MAX_STR.size() && placeholderIndex > SIZE_T_MAX_STR)) {
284 RESMGR_HILOGE(RESMGR_TAG, "index of placeholder is too large");
285 return false;
286 }
287 unsigned long index;
288 if (!Utils::convertToUnsignedLong(placeholderIndex, index) || index < 1) {
289 return false;
290 }
291 paramIndex = index - 1;
292 } else {
293 paramIndex = matchCount++;
294 }
295 return true;
296 }
297
GetPrecision(const std::string & precisionStr)298 int32_t GetPrecision(const std::string &precisionStr)
299 {
300 size_t posOfDecimalPoint = precisionStr.find(".");
301 if (posOfDecimalPoint != std::string::npos) {
302 return std::stoi(precisionStr.substr(posOfDecimalPoint + 1));
303 }
304 return INVALID_PRECISION;
305 }
306
ReplacePlaceholderWithParams(std::string & inputOutputValue,const ResConfigImpl & resConfig,const std::vector<std::tuple<ResourceManager::NapiValueType,std::string>> & jsParams)307 bool ReplacePlaceholderWithParams(std::string &inputOutputValue, const ResConfigImpl &resConfig,
308 const std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)
309 {
310 if (inputOutputValue.empty()) {
311 return true;
312 }
313
314 std::string::const_iterator start = inputOutputValue.begin();
315 std::string::const_iterator end = inputOutputValue.end();
316 std::smatch matches;
317 size_t matchCount = 0;
318 int prefixLength = 0;
319
320 while (std::regex_search(start, end, matches, PLACEHOLDER_MATCHING_RULES)) {
321 prefixLength = matches[MATCHE_INDEX_WHOLE_STRING].first - inputOutputValue.begin();
322 // Matched to %%, replace it with %
323 if (matches[MATCHE_INDEX_DOUBLE_PERCENT].length() != 0) {
324 inputOutputValue.erase(matches[MATCHE_INDEX_DOUBLE_PERCENT].first);
325 start = inputOutputValue.begin() + prefixLength + 1;
326 end = inputOutputValue.end();
327 continue;
328 } else if (jsParams.size() == 0) { // Matched to placeholder but no params, ignore placehold
329 start = inputOutputValue.begin() + prefixLength + matches[0].length();
330 continue;
331 }
332
333 // Matched to placeholder, check and parse param index
334 std::string placeholderIndex = matches[MATCHE_INDEX_PLACEHOLDER_INDEX];
335 size_t paramIndex;
336 if (!MatchPlaceholderIndex(placeholderIndex, paramIndex, matchCount)) {
337 return false;
338 }
339 if (paramIndex >= jsParams.size()) {
340 RESMGR_HILOGE(RESMGR_TAG, "index of placeholder out of range");
341 return false;
342 }
343 // Replace placeholder with corresponding param
344 std::string replaceStr;
345 int32_t precision = GetPrecision(matches[MATCHE_INDEX_PRECISION]);
346 std::string placeholderType = matches[MATCHE_INDEX_PLACEHOLDER_TYPE];
347 if (!GetReplaceStr(jsParams[paramIndex], placeholderType, precision, resConfig, replaceStr)) {
348 return false;
349 }
350 inputOutputValue.replace(prefixLength, matches[MATCHE_INDEX_WHOLE_STRING].length(), replaceStr);
351
352 // Update iterator
353 start = inputOutputValue.begin() + prefixLength + replaceStr.length();
354 end = inputOutputValue.end();
355 }
356 return true;
357 }
358 } // namespace Resource
359 } // namespace Global
360 } // namespace OHOS