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