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 "locale_matcher.h"
27 #include "res_config.h"
28 #include "rstate.h"
29 #include "utils/common.h"
30 #include "utils/utils.h"
31
32 namespace OHOS {
33 namespace Global {
34 namespace Resource {
35 #ifdef SUPPORT_GRAPHICS
36 Locale *ResLocale::defaultLocale_ = nullptr;
37 #endif
38 std::mutex ResLocale::mutex_;
39
ResLocale()40 ResLocale::ResLocale() : language_(nullptr), region_(nullptr), script_(nullptr)
41 {
42 }
43
SetLanguage(const char * language,size_t len)44 RState ResLocale::SetLanguage(const char *language, size_t len)
45 {
46 if (len == 0) {
47 delete[] this->language_;
48 this->language_ = nullptr;
49 return SUCCESS;
50 }
51 char *temp = new(std::nothrow) char[len + 1];
52 if (temp == nullptr) {
53 return NOT_ENOUGH_MEM;
54 }
55 delete[] this->language_;
56 this->language_ = temp;
57 size_t i = 0;
58 while (i < len) {
59 *(temp + i) = tolower(*(language + i));
60 ++i;
61 }
62 *(temp + len) = '\0';
63 return SUCCESS;
64 }
65
SetRegion(const char * region,size_t len)66 RState ResLocale::SetRegion(const char *region, size_t len)
67 {
68 if (len == 0) {
69 delete[] this->region_;
70 this->region_ = nullptr;
71 return SUCCESS;
72 }
73 char *temp = new(std::nothrow) char[len + 1];
74 if (temp == nullptr) {
75 return NOT_ENOUGH_MEM;
76 }
77 delete[] this->region_;
78 this->region_ = temp;
79 size_t i = 0;
80 while (i < len) {
81 *(temp + i) = toupper(*(region + i));
82 ++i;
83 }
84 *(temp + len) = '\0';
85 return SUCCESS;
86 }
87
SetScript(const char * script,size_t len)88 RState ResLocale::SetScript(const char *script, size_t len)
89 {
90 if (len == 0) {
91 delete[] this->script_;
92 this->script_ = nullptr;
93 return SUCCESS;
94 }
95 char *temp = new(std::nothrow) char[len + 1];
96 if (temp == nullptr) {
97 return NOT_ENOUGH_MEM;
98 }
99 delete[] this->script_;
100 this->script_ = temp;
101 size_t i = 0;
102 while (i < len) {
103 if (i == 0) {
104 *(temp + i) = toupper(*(script + i));
105 } else {
106 *(temp + i) = tolower(*(script + i));
107 }
108 ++i;
109 }
110 *(temp + len) = '\0';
111 return SUCCESS;
112 }
113
Init(const char * language,size_t languageLen,const char * script,size_t scriptLen,const char * region,size_t regionLen)114 RState ResLocale::Init(const char *language, size_t languageLen, const char *script, size_t scriptLen,
115 const char *region, size_t regionLen)
116 {
117 RState r = this->SetLanguage(language, languageLen);
118 if (r != SUCCESS) {
119 return r;
120 }
121 r = this->SetScript(script, scriptLen);
122 if (r != SUCCESS) {
123 return r;
124 }
125 r = this->SetRegion(region, regionLen);
126 if (r != SUCCESS) {
127 return r;
128 }
129 return SUCCESS;
130 }
131
132 #ifdef SUPPORT_GRAPHICS
CopyFromLocaleInfo(const Locale * other)133 RState ResLocale::CopyFromLocaleInfo(const Locale *other)
134 {
135 if (other == nullptr) {
136 return ERROR;
137 }
138 return this->Init(other->getLanguage(), Utils::StrLen(other->getLanguage()), other->getScript(),
139 Utils::StrLen(other->getScript()), other->getCountry(), Utils::StrLen(other->getCountry()));
140 }
141 #endif
142
Copy(const ResLocale * other)143 RState ResLocale::Copy(const ResLocale *other)
144 {
145 if (other == nullptr) {
146 return ERROR;
147 }
148 return this->Init(other->GetLanguage(), Utils::StrLen(other->GetLanguage()), other->GetScript(),
149 Utils::StrLen(other->GetScript()), other->GetRegion(), Utils::StrLen(other->GetRegion()));
150 }
151
GetLanguage() const152 const char *ResLocale::GetLanguage() const
153 {
154 return this->language_;
155 }
156
GetRegion() const157 const char *ResLocale::GetRegion() const
158 {
159 return this->region_;
160 }
161
GetScript() const162 const char *ResLocale::GetScript() const
163 {
164 return this->script_;
165 }
166
ProcessSubtag(const char * curPos,int32_t subTagLen,uint16_t & nextType,ParseResult & r)167 RState ProcessSubtag(const char *curPos, int32_t subTagLen, uint16_t &nextType, ParseResult &r)
168 {
169 if ((ResLocale::LANG_TYPE & nextType) && (LocaleMatcher::IsLanguageTag(curPos, subTagLen))) {
170 r.tempLanguage = curPos;
171 r.languageTagLen = subTagLen;
172 nextType = ResLocale::SCRIPT_TYPE | ResLocale::REGION_TYPE;
173 return SUCCESS;
174 }
175 if ((ResLocale::SCRIPT_TYPE & nextType) && LocaleMatcher::IsScriptTag(curPos, subTagLen)) {
176 r.tempScript = curPos;
177 r.scriptTagLen = subTagLen;
178 nextType = ResLocale::REGION_TYPE;
179 return SUCCESS;
180 }
181 if ((ResLocale::REGION_TYPE & nextType) && LocaleMatcher::IsRegionTag(curPos, subTagLen)) {
182 r.tempRegion = curPos;
183 r.regionTagLen = subTagLen;
184 nextType = ResLocale::END_TYPE;
185 return SUCCESS;
186 }
187 return ERROR;
188 }
189
CheckArg(char sep,RState & rState)190 void CheckArg(char sep, RState &rState)
191 {
192 rState = SUCCESS;
193 if (sep != DASH_SEP && sep != UNDERLINE_SEP) {
194 rState = NOT_SUPPORT_SEP;
195 }
196 }
197
CreateResLocale(ParseResult & r,RState & rState)198 ResLocale *ResLocale::CreateResLocale(ParseResult &r, RState &rState)
199 {
200 ResLocale *resLocale = new(std::nothrow) ResLocale;
201 if (resLocale == nullptr) {
202 rState = NOT_ENOUGH_MEM;
203 return nullptr;
204 }
205 rState = resLocale->Init(r.tempLanguage, r.languageTagLen, r.tempScript, r.scriptTagLen,
206 r.tempRegion, r.regionTagLen);
207 if (rState == SUCCESS) {
208 return resLocale;
209 }
210 delete resLocale;
211 return nullptr;
212 }
213
DoParse(const char * str,char sep,RState & rState)214 ResLocale *ResLocale::DoParse(const char *str, char sep, RState &rState)
215 {
216 uint16_t nextType = LANG_TYPE;
217 const char *nextPos = str;
218 const char *curPos = nullptr;
219 ParseResult r;
220 while (nextPos) {
221 if (nextType == END_TYPE) {
222 break;
223 }
224 const char *pSep = nextPos;
225 curPos = nextPos;
226 while (*pSep) {
227 if (*pSep == sep) {
228 break;
229 }
230 pSep++;
231 }
232 nextPos = ((*pSep == 0) ? nullptr : (pSep + 1));
233 int16_t subTagLen = pSep - curPos;
234 if (nextType & LANG_TYPE) {
235 rState = ProcessSubtag(curPos, subTagLen, nextType, r);
236 if (rState == SUCCESS) {
237 continue;
238 }
239 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
240 return nullptr;
241 }
242 if (nextType & SCRIPT_TYPE) {
243 rState = ProcessSubtag(curPos, subTagLen, nextType, r);
244 if (rState == SUCCESS) {
245 continue;
246 }
247 rState = INVALID_BCP47_SCRIPT_SUBTAG;
248 return nullptr;
249 }
250 if (nextType & REGION_TYPE) {
251 rState = ProcessSubtag(curPos, subTagLen, nextType, r);
252 if (rState == SUCCESS) {
253 continue;
254 }
255 rState = INVALID_BCP47_REGION_SUBTAG;
256 return nullptr;
257 }
258 }
259 return CreateResLocale(r, rState);
260 }
261
BuildFromString(const char * str,char sep,RState & rState)262 ResLocale *ResLocale::BuildFromString(const char *str, char sep, RState &rState)
263 {
264 CheckArg(sep, rState);
265 if (rState != SUCCESS) {
266 return nullptr;
267 }
268 size_t strLen = Utils::StrLen(str);
269 if (strLen == 0) {
270 return nullptr;
271 }
272 return DoParse(str, sep, rState);
273 } // end of ParseBCP47Tag
274
BuildFromParts(const char * language,const char * script,const char * region,RState & rState)275 ResLocale *ResLocale::BuildFromParts(const char *language, const char *script, const char *region, RState &rState)
276 {
277 size_t len = Utils::StrLen(language);
278 if (len == 0) {
279 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
280 return nullptr;
281 }
282
283 const char *tempLanguage = nullptr;
284 const char *tempScript = nullptr;
285 const char *tempRegion = nullptr;
286 size_t languageTagLen = 0;
287 size_t scriptTagLen = 0;
288 size_t regionTagLen = 0;
289 if (!LocaleMatcher::IsLanguageTag(language, len)) {
290 rState = INVALID_BCP47_LANGUAGE_SUBTAG;
291 return nullptr;
292 }
293 tempLanguage = language;
294 languageTagLen = len;
295
296 len = Utils::StrLen(script);
297 if (len > 0) {
298 if (!LocaleMatcher::IsScriptTag(script, len)) {
299 rState = INVALID_BCP47_SCRIPT_SUBTAG;
300 return nullptr;
301 }
302 tempScript = script;
303 scriptTagLen = len;
304 }
305 len = Utils::StrLen(region);
306 if (len > 0) {
307 if (!LocaleMatcher::IsRegionTag(region, len)) {
308 rState = INVALID_BCP47_REGION_SUBTAG;
309 return nullptr;
310 }
311 tempRegion = region;
312 regionTagLen = len;
313 }
314 ResLocale *resLocale = new(std::nothrow) ResLocale;
315 if (resLocale == nullptr) {
316 rState = NOT_ENOUGH_MEM;
317 return nullptr;
318 }
319 rState = resLocale->Init(tempLanguage, languageTagLen, tempScript, scriptTagLen, tempRegion, regionTagLen);
320 if (rState == SUCCESS) {
321 return resLocale;
322 }
323 delete resLocale;
324 return nullptr;
325 };
326
327 #ifdef SUPPORT_GRAPHICS
GetDefault()328 const Locale *ResLocale::GetDefault()
329 {
330 std::lock_guard<std::mutex> lock(ResLocale::mutex_);
331 return ResLocale::defaultLocale_;
332 }
333
UpdateDefault(const Locale & localeInfo,bool needNotify)334 bool ResLocale::UpdateDefault(const Locale &localeInfo, bool needNotify)
335 {
336 std::lock_guard<std::mutex> lock(ResLocale::mutex_);
337 UErrorCode errCode = U_ZERO_ERROR;
338 Locale temp = icu::LocaleBuilder().setLocale(localeInfo).build(errCode);
339 if (!U_SUCCESS(errCode)) {
340 return false;
341 }
342 delete ResLocale::defaultLocale_;
343 ResLocale::defaultLocale_ = new Locale(temp);
344 return true;
345 };
346 #endif
347
~ResLocale()348 ResLocale::~ResLocale()
349 {
350 if (this->language_ != nullptr) {
351 delete[] this->language_;
352 this->language_ = nullptr;
353 }
354
355 if (this->script_ != nullptr) {
356 delete[] this->script_;
357 this->script_ = nullptr;
358 }
359
360 if (this->region_ != nullptr) {
361 delete[] this->region_;
362 this->region_ = nullptr;
363 }
364 }
365
366 #ifdef SUPPORT_GRAPHICS
BuildFromString(const char * str,char sep,RState & rState)367 Locale *BuildFromString(const char *str, char sep, RState &rState)
368 {
369 ResLocale *resLocale = ResLocale::BuildFromString(str, sep, rState);
370 if (rState == SUCCESS && resLocale != nullptr) {
371 UErrorCode errCode = U_ZERO_ERROR;
372 Locale temp = icu::LocaleBuilder().setLanguage(resLocale->GetLanguage())
373 .setRegion(resLocale->GetRegion()).setScript(resLocale->GetScript()).build(errCode);
374
375 if (!U_SUCCESS(errCode)) {
376 delete resLocale;
377 rState = ERROR;
378 return nullptr;
379 }
380 Locale *retLocal = new Locale(temp);
381 delete resLocale;
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