• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &paramIndex, 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