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 if (nLength < 0) {
77 return strResult;
78 }
79 std::vector<char> vBuffer(nLength + 1, '\0');
80 int nWritten = vsnprintf_s(&vBuffer[0], nLength + 1, nLength, fmt, args);
81 if (nWritten > 0) {
82 strResult = &vBuffer[0];
83 }
84 }
85 return strResult;
86 }
87
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)88 bool getJsParams(const std::string &inputOutputValue, va_list args,
89 std::vector<std::pair<int, std::string>> paramsWithOutNum,
90 std::vector<std::pair<int, std::string>> paramsWithNum,
91 std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)
92 {
93 std::sort(paramsWithNum.begin(), paramsWithNum.end(),
94 [](const std::pair<int, std::string>& a, const std::pair<int, std::string>& b) {
95 return a.first < b.first;
96 });
97 for (size_t i = 0; i < paramsWithOutNum.size(); i++) {
98 std::string type = paramsWithOutNum[i].second;
99 if (type == "d") {
100 int temp = va_arg(args, int);
101 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
102 } else if (type == "s") {
103 char *temp = va_arg(args, char*);
104 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_STRING, temp);
105 } else if (type == "f") {
106 float temp = va_arg(args, double);
107 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
108 }
109 }
110 for (size_t i = 0; i < paramsWithNum.size(); i++) {
111 size_t index = static_cast<size_t>(paramsWithNum[i].first);
112 std::string type = paramsWithNum[i].second;
113 if (index < paramsWithOutNum.size()) {
114 if (type != paramsWithOutNum[index].second) {
115 return false;
116 }
117 } else if (index == paramsWithOutNum.size()) {
118 paramsWithOutNum.push_back({index, type});
119 if (type == "d") {
120 int temp = va_arg(args, int);
121 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
122 } else if (type == "s") {
123 char *temp = va_arg(args, char*);
124 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_STRING, temp);
125 } else if (type == "f") {
126 float temp = va_arg(args, double);
127 jsParams.emplace_back(ResourceManager::NapiValueType::NAPI_NUMBER, std::to_string(temp));
128 }
129 } else {
130 return false;
131 }
132 }
133 return true;
134 }
135
parseArgs(const std::string & inputOutputValue,va_list args,std::vector<std::tuple<ResourceManager::NapiValueType,std::string>> & jsParams)136 bool parseArgs(const std::string &inputOutputValue, va_list args,
137 std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)
138 {
139 if (inputOutputValue.empty()) {
140 return true;
141 }
142 std::string::const_iterator start = inputOutputValue.begin();
143 std::string::const_iterator end = inputOutputValue.end();
144 std::smatch matches;
145 size_t matchCount = 0;
146 int prefixLength = 0;
147 int offset = 2;
148 std::vector<std::pair<int, std::string>> paramsWithOutNum;
149 std::vector<std::pair<int, std::string>> paramsWithNum;
150 while (std::regex_search(start, end, matches, PLACEHOLDER_MATCHING_RULES)) {
151 prefixLength = matches[MATCHE_INDEX_WHOLE_STRING].first - inputOutputValue.begin();
152 if (matches[1].length() != 0) {
153 start = inputOutputValue.begin() + prefixLength + offset;
154 continue;
155 }
156 std::string placeholderIndex = matches[MATCHE_INDEX_PLACEHOLDER_INDEX];
157 std::string placeholderType = matches[MATCHE_INDEX_PLACEHOLDER_TYPE];
158 size_t paramIndex;
159 if (placeholderIndex.length() != 0) {
160 if (placeholderIndex.size() > SIZE_T_MAX_STR.size() ||
161 (placeholderIndex.size() == SIZE_T_MAX_STR.size() && placeholderIndex > SIZE_T_MAX_STR)) {
162 RESMGR_HILOGE(RESMGR_TAG, "index of placeholder is too large");
163 return false;
164 }
165 unsigned long index;
166 if (!Utils::convertToUnsignedLong(placeholderIndex, index) || index < 1) {
167 RESMGR_HILOGE(RESMGR_TAG, "convert value error, placeholderIndex = %{public}s",
168 placeholderIndex.c_str());
169 return false;
170 }
171 paramIndex = index - 1;
172 paramsWithNum.push_back({paramIndex, placeholderType});
173 } else {
174 paramIndex = matchCount++;
175 paramsWithOutNum.push_back({paramIndex, placeholderType});
176 }
177 start = inputOutputValue.begin() + prefixLength + matches[0].length();
178 }
179 return getJsParams(inputOutputValue, args, paramsWithOutNum, paramsWithNum, jsParams);
180 }
181
GetLocalInfo(const ResLocale * resLocale)182 std::string GetLocalInfo(const ResLocale *resLocale)
183 {
184 std::string localeInfo;
185 const char *language = resLocale->GetLanguage();
186 if (language != nullptr && strlen(language) > 0) {
187 localeInfo.assign(language);
188 } else {
189 RESMGR_HILOGW(RESMGR_TAG, "GetLocalInfo language is null");
190 return localeInfo;
191 }
192 std::string temp;
193 const char *script = resLocale->GetScript();
194 if (script != nullptr && strlen(script) > 0) {
195 temp.assign(script);
196 localeInfo += "-" + temp;
197 }
198 const char *region = resLocale->GetRegion();
199 if (region != nullptr && strlen(region) > 0) {
200 temp.assign(region);
201 localeInfo += "-" + temp;
202 }
203 return localeInfo;
204 }
205
LocalizeNumber(std::string & inputOutputNum,const ResConfigImpl & resConfig,const int32_t precision=INVALID_PRECISION)206 bool LocalizeNumber(std::string &inputOutputNum, const ResConfigImpl &resConfig,
207 const int32_t precision = INVALID_PRECISION)
208 {
209 #ifdef SUPPORT_GRAPHICS
210 const ResLocale *resLocale = resConfig.GetResLocale();
211 if (resLocale == nullptr) {
212 RESMGR_HILOGW(RESMGR_TAG, "LocalizeNumber resLocale is null");
213 return true;
214 }
215
216 std::string localeInfo = GetLocalInfo(resLocale);
217 if (localeInfo.empty()) {
218 return true;
219 }
220 icu::Locale locale(localeInfo.c_str());
221 if (locale.isBogus()) {
222 return true;
223 }
224
225 icu::number::LocalizedNumberFormatter numberFormat = icu::number::NumberFormatter::withLocale(locale);
226 numberFormat = numberFormat.grouping(UNumberGroupingStrategy::UNUM_GROUPING_OFF);
227 numberFormat = numberFormat.roundingMode(UNUM_ROUND_HALFUP);
228 if (precision != INVALID_PRECISION) {
229 numberFormat = numberFormat.precision(icu::number::Precision::fixedFraction(precision));
230 }
231 double num;
232 if (!Utils::convertToDouble(inputOutputNum, num)) {
233 return false;
234 }
235 inputOutputNum.clear();
236 UErrorCode status = U_ZERO_ERROR;
237 icu::UnicodeString formattedNum = numberFormat.formatDouble(num, status).toString(status);
238 if (U_FAILURE(status)) {
239 RESMGR_HILOGE(RESMGR_TAG, "LocalizeNumber formatDouble failed, status = %{public}d", status);
240 return false;
241 }
242 formattedNum.toUTF8String(inputOutputNum);
243 return true;
244 #else
245 return true;
246 #endif
247 }
248
GetReplaceStr(const std::tuple<ResourceManager::NapiValueType,std::string> & jsParam,const std::string & placeHolderType,int32_t precision,const ResConfigImpl & config,std::string & replaceStr)249 bool GetReplaceStr(const std::tuple<ResourceManager::NapiValueType, std::string> &jsParam,
250 const std::string &placeHolderType, int32_t precision, const ResConfigImpl &config, std::string &replaceStr)
251 {
252 ResourceManager::NapiValueType paramType = std::get<0>(jsParam);
253 std::string paramValue = std::get<1>(jsParam);
254
255 // string type
256 if (placeHolderType == "s") {
257 if (paramType != ResourceManager::NapiValueType::NAPI_STRING) {
258 RESMGR_HILOGE(RESMGR_TAG, "the type of placeholder and param does not match");
259 return false;
260 }
261 replaceStr = paramValue;
262 return true;
263 }
264
265 // number type
266 if (paramType != ResourceManager::NapiValueType::NAPI_NUMBER) {
267 RESMGR_HILOGE(RESMGR_TAG, "the type of placeholder and param does not match");
268 return false;
269 }
270
271 // int type
272 if (placeHolderType == "d") {
273 size_t posOfDecimalPoint = paramValue.find(".");
274 replaceStr = paramValue.substr(0, posOfDecimalPoint);
275 return LocalizeNumber(replaceStr, config);
276 }
277
278 // double type
279 replaceStr = paramValue;
280 return LocalizeNumber(replaceStr, config, precision);
281 }
282
MatchPlaceholderIndex(std::string placeholderIndex,size_t & paramIndex,size_t & matchCount)283 bool MatchPlaceholderIndex(std::string placeholderIndex, size_t ¶mIndex, size_t &matchCount)
284 {
285 if (placeholderIndex.length() != 0) {
286 if (placeholderIndex.size() > SIZE_T_MAX_STR.size() ||
287 (placeholderIndex.size() == SIZE_T_MAX_STR.size() && placeholderIndex > SIZE_T_MAX_STR)) {
288 RESMGR_HILOGE(RESMGR_TAG, "index of placeholder is too large");
289 return false;
290 }
291 unsigned long index;
292 if (!Utils::convertToUnsignedLong(placeholderIndex, index) || index < 1) {
293 return false;
294 }
295 paramIndex = index - 1;
296 } else {
297 paramIndex = matchCount++;
298 }
299 return true;
300 }
301
GetPrecision(const std::string & precisionStr)302 int32_t GetPrecision(const std::string &precisionStr)
303 {
304 size_t posOfDecimalPoint = precisionStr.find(".");
305 if (posOfDecimalPoint != std::string::npos) {
306 return std::stoi(precisionStr.substr(posOfDecimalPoint + 1));
307 }
308 return INVALID_PRECISION;
309 }
310
ReplacePlaceholderWithParams(std::string & inputOutputValue,const ResConfigImpl & resConfig,const std::vector<std::tuple<ResourceManager::NapiValueType,std::string>> & jsParams)311 bool ReplacePlaceholderWithParams(std::string &inputOutputValue, const ResConfigImpl &resConfig,
312 const std::vector<std::tuple<ResourceManager::NapiValueType, std::string>> &jsParams)
313 {
314 if (inputOutputValue.empty()) {
315 return true;
316 }
317
318 std::string::const_iterator start = inputOutputValue.begin();
319 std::string::const_iterator end = inputOutputValue.end();
320 std::smatch matches;
321 size_t matchCount = 0;
322 int prefixLength = 0;
323
324 while (std::regex_search(start, end, matches, PLACEHOLDER_MATCHING_RULES)) {
325 prefixLength = matches[MATCHE_INDEX_WHOLE_STRING].first - inputOutputValue.begin();
326 // Matched to %%, replace it with %
327 if (matches[MATCHE_INDEX_DOUBLE_PERCENT].length() != 0) {
328 inputOutputValue.erase(matches[MATCHE_INDEX_DOUBLE_PERCENT].first);
329 start = inputOutputValue.begin() + prefixLength + 1;
330 end = inputOutputValue.end();
331 continue;
332 } else if (jsParams.size() == 0) { // Matched to placeholder but no params, ignore placehold
333 start = inputOutputValue.begin() + prefixLength + matches[0].length();
334 continue;
335 }
336
337 // Matched to placeholder, check and parse param index
338 std::string placeholderIndex = matches[MATCHE_INDEX_PLACEHOLDER_INDEX];
339 size_t paramIndex;
340 if (!MatchPlaceholderIndex(placeholderIndex, paramIndex, matchCount)) {
341 return false;
342 }
343 if (paramIndex >= jsParams.size()) {
344 RESMGR_HILOGE(RESMGR_TAG, "index of placeholder out of range");
345 return false;
346 }
347 // Replace placeholder with corresponding param
348 std::string replaceStr;
349 int32_t precision = GetPrecision(matches[MATCHE_INDEX_PRECISION]);
350 std::string placeholderType = matches[MATCHE_INDEX_PLACEHOLDER_TYPE];
351 if (!GetReplaceStr(jsParams[paramIndex], placeholderType, precision, resConfig, replaceStr)) {
352 return false;
353 }
354 inputOutputValue.replace(prefixLength, matches[MATCHE_INDEX_WHOLE_STRING].length(), replaceStr);
355
356 // Update iterator
357 start = inputOutputValue.begin() + prefixLength + replaceStr.length();
358 end = inputOutputValue.end();
359 }
360 return true;
361 }
362 } // namespace Resource
363 } // namespace Global
364 } // namespace OHOS