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, const char *script, const char *region, RState &rState)
277 {
278 size_t len = Utils::StrLen(language);
279 if (len == 0) {
280 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
281 return nullptr;
282 }
283
284 const char *tempLanguage = nullptr;
285 const char *tempScript = nullptr;
286 const char *tempRegion = nullptr;
287 size_t languageTagLen = 0;
288 size_t scriptTagLen = 0;
289 size_t regionTagLen = 0;
290 if (!LocaleMatcher::IsLanguageTag(language, len)) {
291 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
292 return nullptr;
293 }
294 tempLanguage = language;
295 languageTagLen = len;
296
297 len = Utils::StrLen(script);
298 if (len > 0) {
299 if (!LocaleMatcher::IsScriptTag(script, len)) {
300 rState = INVALID_BCP47_SCRIPT_SUBTAG;
301 return nullptr;
302 }
303 tempScript = script;
304 scriptTagLen = len;
305 }
306 len = Utils::StrLen(region);
307 if (len > 0) {
308 if (!LocaleMatcher::IsRegionTag(region, len)) {
309 rState = INVALID_BCP47_REGION_SUBTAG;
310 return nullptr;
311 }
312 tempRegion = region;
313 regionTagLen = len;
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
328 #ifdef SUPPORT_GRAPHICS
GetDefault()329 const Locale *ResLocale::GetDefault()
330 {
331 AutoMutex mutex(ResLocale::lock_);
332 return ResLocale::defaultLocale_;
333 }
334
UpdateDefault(const Locale & localeInfo,bool needNotify)335 bool ResLocale::UpdateDefault(const Locale &localeInfo, bool needNotify)
336 {
337 AutoMutex mutex(ResLocale::lock_);
338 UErrorCode errCode = U_ZERO_ERROR;
339 Locale temp = icu::LocaleBuilder().setLocale(localeInfo).build(errCode);
340 if (!U_SUCCESS(errCode)) {
341 return false;
342 }
343 delete ResLocale::defaultLocale_;
344 ResLocale::defaultLocale_ = new Locale(temp);
345 return true;
346 };
347 #endif
348
~ResLocale()349 ResLocale::~ResLocale()
350 {
351 if (this->language_ != nullptr) {
352 delete[] this->language_;
353 this->language_ = nullptr;
354 }
355
356 if (this->script_ != nullptr) {
357 delete[] this->script_;
358 this->script_ = nullptr;
359 }
360
361 if (this->region_ != nullptr) {
362 delete[] this->region_;
363 this->region_ = nullptr;
364 }
365 }
366
367 #ifdef SUPPORT_GRAPHICS
BuildFromString(const char * str,char sep,RState & rState)368 Locale *BuildFromString(const char *str, char sep, RState &rState)
369 {
370 ResLocale *resLocale = ResLocale::BuildFromString(str, sep, rState);
371 if (rState == SUCCESS && resLocale != nullptr) {
372 UErrorCode errCode = U_ZERO_ERROR;
373 Locale temp = icu::LocaleBuilder().setLanguage(resLocale->GetLanguage())
374 .setRegion(resLocale->GetRegion()).setScript(resLocale->GetScript()).build(errCode);
375
376 if (!U_SUCCESS(errCode)) {
377 delete resLocale;
378 rState = ERROR;
379 return nullptr;
380 }
381 Locale *retLocal = new Locale(temp);
382 return retLocal;
383 }
384 return nullptr;
385 };
386
BuildFromParts(const char * language,const char * script,const char * region,RState & rState)387 Locale *BuildFromParts(const char *language, const char *script, const char *region, RState &rState)
388 {
389 size_t len = Utils::StrLen(language);
390 if (len == 0) {
391 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
392 return nullptr;
393 }
394 if (!(LocaleMatcher::IsLanguageTag(language, len))) {
395 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
396 return nullptr;
397 }
398
399 len = Utils::StrLen(script);
400 if (len > 0) {
401 if (LocaleMatcher::IsScriptTag(script, len) == 0) {
402 rState = INVALID_BCP47_SCRIPT_SUBTAG;
403 return nullptr;
404 }
405 }
406 len = Utils::StrLen(region);
407 if (len > 0) {
408 if (LocaleMatcher::IsRegionTag(region, len) == 0) {
409 rState = INVALID_BCP47_REGION_SUBTAG;
410 return nullptr;
411 }
412 }
413 UErrorCode errCode = U_ZERO_ERROR;
414 Locale localeInfo = icu::LocaleBuilder().setLanguage(language)
415 .setRegion(region).setScript(script).build(errCode);
416 if (!U_SUCCESS(errCode)) {
417 rState = ERROR;
418 return nullptr;
419 }
420 Locale *retLocal = new Locale(localeInfo);
421 return retLocal;
422 }
423
GetSysDefault()424 const Locale *GetSysDefault()
425 {
426 return ResLocale::GetDefault();
427 }
428
UpdateSysDefault(const Locale & localeInfo,bool needNotify)429 void UpdateSysDefault(const Locale &localeInfo, bool needNotify)
430 {
431 ResLocale::UpdateDefault(localeInfo, needNotify);
432 }
433 #endif
434
FindAndSort(const std::string localeStr,std::vector<std::string> & candidateLocale,std::vector<std::string> & outValue)435 void FindAndSort(const std::string localeStr, std::vector<std::string> &candidateLocale,
436 std::vector<std::string> &outValue)
437 {
438 if (candidateLocale.size() == 0) {
439 return;
440 }
441 std::vector<ResLocale *> tempCandidate;
442 RState state = SUCCESS;
443 ResLocale *currentLocale = ResLocale::BuildFromString(localeStr.c_str(), DASH_SEP, state);
444 LocaleMatcher::Normalize(currentLocale);
445 std::vector<std::string>::const_iterator iter;
446 for (iter = candidateLocale.cbegin(); iter != candidateLocale.cend(); ++iter) {
447 ResLocale *resLocale = ResLocale::BuildFromString(iter->c_str(), DASH_SEP, state);
448 if (state == SUCCESS) {
449 LocaleMatcher::Normalize(resLocale);
450 bool isMatch = LocaleMatcher::Match(currentLocale, resLocale);
451 if (isMatch) {
452 tempCandidate.push_back(resLocale);
453 outValue.push_back(*iter);
454 } else {
455 delete resLocale;
456 }
457 } else {
458 delete resLocale;
459 }
460 }
461 // sort
462 std::size_t len = tempCandidate.size();
463 if (len == 0) {
464 delete currentLocale;
465 return;
466 }
467 for (std::size_t i = 0; i < len - 1; i++) {
468 for (std::size_t j = 0; j < len - 1 - i; j++) {
469 if (LocaleMatcher::IsMoreSuitable(tempCandidate.at(j), tempCandidate.at(j + 1), currentLocale) <= 0) {
470 ResLocale *temp = tempCandidate.at(j + 1);
471 tempCandidate.at(j + 1) = tempCandidate.at(j);
472 tempCandidate.at(j) = temp;
473 std::string tempStr = outValue.at(j + 1);
474 outValue.at(j + 1) = outValue.at(j);
475 outValue.at(j) = tempStr;
476 }
477 }
478 }
479
480 for (auto iter = tempCandidate.cbegin(); iter != tempCandidate.cend(); iter++) {
481 delete *iter;
482 }
483 delete currentLocale;
484 }
485 } // namespace Resource
486 } // namespace Global
487 } // namespace OHOS