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 "res_locale.h"
17
18 #include <cctype>
19 #include <cstdint>
20 #include <cstring>
21 #include <new>
22
23 #include "auto_mutex.h"
24 #include "hilog_wrapper.h"
25 #include "locale_matcher.h"
26 #include "res_config.h"
27 #include "rstate.h"
28 #include "utils/common.h"
29 #include "utils/utils.h"
30
31 namespace OHOS {
32 namespace Global {
33 namespace Resource {
34 LocaleInfo *ResLocale::defaultLocale_ = nullptr;
35 Lock ResLocale::lock_;
36
ResLocale()37 ResLocale::ResLocale() : language_(nullptr), region_(nullptr), script_(nullptr)
38 {
39 }
40
SetLanguage(const char * language,size_t len)41 RState ResLocale::SetLanguage(const char *language, size_t len)
42 {
43 if (len == 0) {
44 delete this->language_;
45 this->language_ = nullptr;
46 return SUCCESS;
47 }
48 char *temp = new(std::nothrow) char[len + 1];
49 if (temp == nullptr) {
50 return NOT_ENOUGH_MEM;
51 }
52 delete this->language_;
53 this->language_ = temp;
54 size_t i = 0;
55 while (i < len) {
56 *(temp + i) = tolower(*(language + i));
57 ++i;
58 }
59 *(temp + len) = '\0';
60 return SUCCESS;
61 }
62
SetRegion(const char * region,size_t len)63 RState ResLocale::SetRegion(const char *region, size_t len)
64 {
65 if (len == 0) {
66 delete this->region_;
67 this->region_ = nullptr;
68 return SUCCESS;
69 }
70 char *temp = new(std::nothrow) char[len + 1];
71 if (temp == nullptr) {
72 return NOT_ENOUGH_MEM;
73 }
74 delete this->region_;
75 this->region_ = temp;
76 size_t i = 0;
77 while (i < len) {
78 *(temp + i) = toupper(*(region + i));
79 ++i;
80 }
81 *(temp + len) = '\0';
82 return SUCCESS;
83 }
84
SetScript(const char * script,size_t len)85 RState ResLocale::SetScript(const char *script, size_t len)
86 {
87 if (len == 0) {
88 delete this->script_;
89 this->script_ = nullptr;
90 return SUCCESS;
91 }
92 char *temp = new(std::nothrow) char[len + 1];
93 if (temp == nullptr) {
94 return NOT_ENOUGH_MEM;
95 }
96 delete this->script_;
97 this->script_ = temp;
98 size_t i = 0;
99 while (i < len) {
100 if (i == 0) {
101 *(temp + i) = toupper(*(script + i));
102 } else {
103 *(temp + i) = tolower(*(script + i));
104 }
105 ++i;
106 }
107 *(temp + len) = '\0';
108 return SUCCESS;
109 }
110
Init(const char * language,size_t languageLen,const char * script,size_t scriptLen,const char * region,size_t regionLen)111 RState ResLocale::Init(const char *language, size_t languageLen, const char *script, size_t scriptLen,
112 const char *region, size_t regionLen)
113 {
114 RState r = this->SetLanguage(language, languageLen);
115 if (r != SUCCESS) {
116 return r;
117 }
118 r = this->SetScript(script, scriptLen);
119 if (r != SUCCESS) {
120 return r;
121 }
122 r = this->SetRegion(region, regionLen);
123 if (r != SUCCESS) {
124 return r;
125 }
126 return SUCCESS;
127 }
128
CopyFromLocaleInfo(const LocaleInfo * other)129 RState ResLocale::CopyFromLocaleInfo(const LocaleInfo *other)
130 {
131 if (other == nullptr) {
132 return ERROR;
133 }
134 return this->Init(other->GetLanguage(), Utils::StrLen(other->GetLanguage()), other->GetScript(),
135 Utils::StrLen(other->GetScript()), other->GetRegion(), Utils::StrLen(other->GetRegion()));
136 }
137
Copy(const ResLocale * other)138 RState ResLocale::Copy(const ResLocale *other)
139 {
140 if (other == nullptr) {
141 return ERROR;
142 }
143 return this->Init(other->GetLanguage(), Utils::StrLen(other->GetLanguage()), other->GetScript(),
144 Utils::StrLen(other->GetScript()), other->GetRegion(), Utils::StrLen(other->GetRegion()));
145 }
146
GetLanguage() const147 const char *ResLocale::GetLanguage() const
148 {
149 return this->language_;
150 }
151
GetRegion() const152 const char *ResLocale::GetRegion() const
153 {
154 return this->region_;
155 }
156
GetScript() const157 const char *ResLocale::GetScript() const
158 {
159 return this->script_;
160 }
161
ProcessSubtag(const char * curPos,int32_t subTagLen,uint16_t & nextType,ParseResult & r)162 RState ProcessSubtag(const char *curPos, int32_t subTagLen, uint16_t &nextType, ParseResult &r)
163 {
164 if ((ResLocale::LANG_TYPE & nextType) && (LocaleMatcher::IsLanguageTag(curPos, subTagLen))) {
165 r.tempLanguage = curPos;
166 r.languageTagLen = subTagLen;
167 nextType = ResLocale::SCRIPT_TYPE | ResLocale::REGION_TYPE;
168 return SUCCESS;
169 }
170 if ((ResLocale::SCRIPT_TYPE & nextType) && LocaleMatcher::IsScriptTag(curPos, subTagLen)) {
171 r.tempScript = curPos;
172 r.scriptTagLen = subTagLen;
173 nextType = ResLocale::REGION_TYPE;
174 return SUCCESS;
175 }
176 if ((ResLocale::REGION_TYPE & nextType) && LocaleMatcher::IsRegionTag(curPos, subTagLen)) {
177 r.tempRegion = curPos;
178 r.regionTagLen = subTagLen;
179 nextType = ResLocale::END_TYPE;
180 return SUCCESS;
181 }
182 return ERROR;
183 }
184
CheckArg(char sep,RState & rState)185 void CheckArg(char sep, RState &rState)
186 {
187 rState = SUCCESS;
188 if (sep != DASH_SEP && sep != UNDERLINE_SEP) {
189 rState = NOT_SUPPORT_SEP;
190 }
191 }
192
CreateResLocale(ParseResult & r,RState & rState)193 ResLocale *ResLocale::CreateResLocale(ParseResult &r, RState &rState)
194 {
195 ResLocale *resLocale = new(std::nothrow) ResLocale;
196 if (resLocale == nullptr) {
197 rState = NOT_ENOUGH_MEM;
198 return nullptr;
199 }
200 rState = resLocale->Init(r.tempLanguage, r.languageTagLen, r.tempScript, r.scriptTagLen,
201 r.tempRegion, r.regionTagLen);
202 if (rState == SUCCESS) {
203 return resLocale;
204 }
205 delete resLocale;
206 return nullptr;
207 }
208
DoParse(const char * str,char sep,RState & rState)209 ResLocale *ResLocale::DoParse(const char *str, char sep, RState &rState)
210 {
211 uint16_t nextType = LANG_TYPE;
212 const char *nextPos = str;
213 const char *curPos = nextPos;
214 ParseResult r;
215 while (nextPos) {
216 if (nextType == END_TYPE) {
217 break;
218 }
219 const char *pSep = nextPos;
220 curPos = nextPos;
221 while (*pSep) {
222 if (*pSep == sep) {
223 break;
224 }
225 pSep++;
226 }
227 nextPos = ((*pSep == 0) ? nullptr : (pSep + 1));
228 int16_t subTagLen = pSep - curPos;
229 if (nextType & LANG_TYPE) {
230 rState = ProcessSubtag(curPos, subTagLen, nextType, r);
231 if (rState == SUCCESS) {
232 continue;
233 }
234 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
235 return nullptr;
236 }
237 if (nextType & SCRIPT_TYPE) {
238 rState = ProcessSubtag(curPos, subTagLen, nextType, r);
239 if (rState == SUCCESS) {
240 continue;
241 }
242 rState = INVALID_BCP47_SCRIPT_SUBTAG;
243 return nullptr;
244 }
245 if (nextType & REGION_TYPE) {
246 rState = ProcessSubtag(curPos, subTagLen, nextType, r);
247 if (rState == SUCCESS) {
248 continue;
249 }
250 rState = INVALID_BCP47_REGION_SUBTAG;
251 return nullptr;
252 }
253 }
254 return CreateResLocale(r, rState);
255 }
256
BuildFromString(const char * str,char sep,RState & rState)257 ResLocale *ResLocale::BuildFromString(const char *str, char sep, RState &rState)
258 {
259 CheckArg(sep, rState);
260 if (rState != SUCCESS) {
261 return nullptr;
262 }
263 size_t strLen = Utils::StrLen(str);
264 if (strLen == 0) {
265 return nullptr;
266 }
267 return DoParse(str, sep, rState);
268 } // end of ParseBCP47Tag
269
BuildFromParts(const char * language,const char * script,const char * region,RState & rState)270 ResLocale *ResLocale::BuildFromParts(const char *language,
271 const char *script,
272 const char *region,
273 RState &rState)
274 {
275 size_t len = Utils::StrLen(language);
276 if (len == 0) {
277 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
278 return nullptr;
279 }
280
281 const char *tempLanguage = nullptr;
282 const char *tempScript = nullptr;
283 const char *tempRegion = nullptr;
284 size_t languageTagLen = 0;
285 size_t scriptTagLen = 0;
286 size_t regionTagLen = 0;
287 if (LocaleMatcher::IsLanguageTag(language, len)) {
288 tempLanguage = language;
289 languageTagLen = len;
290 } else {
291 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
292 return nullptr;
293 }
294
295 len = Utils::StrLen(script);
296 if (len > 0) {
297 if (LocaleMatcher::IsScriptTag(script, len)) {
298 tempScript = script;
299 scriptTagLen = len;
300 } else {
301 rState = INVALID_BCP47_SCRIPT_SUBTAG;
302 return nullptr;
303 }
304 }
305 len = Utils::StrLen(region);
306 if (len > 0) {
307 if (LocaleMatcher::IsRegionTag(region, len)) {
308 tempRegion = region;
309 regionTagLen = len;
310 } else {
311 rState = INVALID_BCP47_REGION_SUBTAG;
312 return nullptr;
313 }
314 }
315 ResLocale *resLocale = new(std::nothrow) ResLocale;
316 if (resLocale == nullptr) {
317 rState = NOT_ENOUGH_MEM;
318 return nullptr;
319 }
320 rState = resLocale->Init(tempLanguage, languageTagLen, tempScript, scriptTagLen, tempRegion, regionTagLen);
321 if (rState == SUCCESS) {
322 return resLocale;
323 }
324 delete resLocale;
325 return nullptr;
326 };
327
GetDefault()328 const LocaleInfo *ResLocale::GetDefault()
329 {
330 AutoMutex mutex(ResLocale::lock_);
331 return ResLocale::defaultLocale_;
332 }
333
UpdateDefault(const LocaleInfo & localeInfo,bool needNotify)334 bool ResLocale::UpdateDefault(const LocaleInfo &localeInfo, bool needNotify)
335 {
336 AutoMutex mutex(ResLocale::lock_);
337 LocaleInfo *temp = new(std::nothrow) LocaleInfo(localeInfo.GetLanguage(),
338 localeInfo.GetScript(), localeInfo.GetRegion());
339 if (temp == nullptr) {
340 return false;
341 }
342 delete ResLocale::defaultLocale_;
343 ResLocale::defaultLocale_ = temp;
344 return true;
345 };
346
~ResLocale()347 ResLocale::~ResLocale()
348 {
349 if (this->language_ != nullptr) {
350 delete[] this->language_;
351 this->language_ = nullptr;
352 }
353
354 if (this->script_ != nullptr) {
355 delete[] this->script_;
356 this->script_ = nullptr;
357 }
358
359 if (this->region_ != nullptr) {
360 delete[] this->region_;
361 this->region_ = nullptr;
362 }
363 }
364
BuildFromString(const char * str,char sep,RState & rState)365 LocaleInfo *BuildFromString(const char *str, char sep, RState &rState)
366 {
367 ResLocale *resLocale = ResLocale::BuildFromString(str, sep, rState);
368 if (rState == SUCCESS) {
369 LocaleInfo *localeInfo = new(std::nothrow) LocaleInfo(resLocale->GetLanguage(),
370 resLocale->GetScript(), resLocale->GetRegion());
371 if (localeInfo == nullptr) {
372 delete resLocale;
373 rState = ERROR;
374 return nullptr;
375 }
376 return localeInfo;
377 }
378 return nullptr;
379 };
380
BuildFromParts(const char * language,const char * script,const char * region,RState & rState)381 LocaleInfo *BuildFromParts(const char *language, const char *script, const char *region, RState &rState)
382 {
383 size_t len = Utils::StrLen(language);
384 if (len == 0) {
385 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
386 return nullptr;
387 }
388 if (!(LocaleMatcher::IsLanguageTag(language, len))) {
389 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
390 return nullptr;
391 }
392
393 len = Utils::StrLen(script);
394 if (len > 0) {
395 if (LocaleMatcher::IsScriptTag(script, len) == 0) {
396 rState = INVALID_BCP47_SCRIPT_SUBTAG;
397 return nullptr;
398 }
399 }
400 len = Utils::StrLen(region);
401 if (len > 0) {
402 if (LocaleMatcher::IsRegionTag(region, len) == 0) {
403 rState = INVALID_BCP47_REGION_SUBTAG;
404 return nullptr;
405 }
406 }
407 LocaleInfo *localeInfo = new(std::nothrow) LocaleInfo(language, script, region);
408 if (localeInfo == nullptr) {
409 rState = ERROR;
410 return nullptr;
411 }
412 return localeInfo;
413 }
414
GetSysDefault()415 const LocaleInfo *GetSysDefault()
416 {
417 return ResLocale::GetDefault();
418 }
419
UpdateSysDefault(const LocaleInfo & localeInfo,bool needNotify)420 void UpdateSysDefault(const LocaleInfo &localeInfo, bool needNotify)
421 {
422 ResLocale::UpdateDefault(localeInfo, needNotify);
423 }
424
FindAndSort(const std::string localeStr,std::vector<std::string> & candidateLocale,std::vector<std::string> & outValue)425 void FindAndSort(const std::string localeStr, std::vector<std::string> &candidateLocale,
426 std::vector<std::string> &outValue)
427 {
428 if (candidateLocale.size() == 0) {
429 return;
430 }
431 std::vector<ResLocale *> tempCandidate;
432 RState state = SUCCESS;
433 ResLocale *currentLocale = ResLocale::BuildFromString(localeStr.c_str(), DASH_SEP, state);
434 LocaleMatcher::Normalize(currentLocale);
435 std::vector<std::string>::const_iterator iter;
436 for (iter = candidateLocale.cbegin(); iter != candidateLocale.cend(); ++iter) {
437 ResLocale *resLocale = ResLocale::BuildFromString(iter->c_str(), DASH_SEP, state);
438 if (state == SUCCESS) {
439 LocaleMatcher::Normalize(resLocale);
440 bool isMatch = LocaleMatcher::Match(currentLocale, resLocale);
441 if (isMatch) {
442 tempCandidate.push_back(resLocale);
443 outValue.push_back(*iter);
444 } else {
445 delete resLocale;
446 }
447 } else {
448 delete resLocale;
449 }
450 }
451 // sort
452 std::size_t len = tempCandidate.size();
453 if (len == 0) {
454 delete currentLocale;
455 return;
456 }
457 for (std::size_t i = 0; i < len - 1; i++) {
458 for (std::size_t j = 0; j < len - 1 - i; j++) {
459 if (LocaleMatcher::IsMoreSuitable(tempCandidate.at(j), tempCandidate.at(j + 1), currentLocale) <= 0) {
460 ResLocale *temp = tempCandidate.at(j + 1);
461 tempCandidate.at(j + 1) = tempCandidate.at(j);
462 tempCandidate.at(j) = temp;
463 std::string tempStr = outValue.at(j + 1);
464 outValue.at(j + 1) = outValue.at(j);
465 outValue.at(j) = tempStr;
466 }
467 }
468 }
469
470 for (auto iter = tempCandidate.cbegin(); iter != tempCandidate.cend(); iter++) {
471 delete *iter;
472 }
473 delete currentLocale;
474 }
475 } // namespace Resource
476 } // namespace Global
477 } // namespace OHOS