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         delete resLocale;
383         return retLocal;
384     }
385     return nullptr;
386 };
387 
BuildFromParts(const char * language,const char * script,const char * region,RState & rState)388 Locale *BuildFromParts(const char *language, const char *script, const char *region, RState &rState)
389 {
390     size_t len = Utils::StrLen(language);
391     if (len == 0) {
392         rState = INVALID_BCP47_LANGUAGE_SUBTAG;
393         return nullptr;
394     }
395     if (!(LocaleMatcher::IsLanguageTag(language, len))) {
396         rState = INVALID_BCP47_LANGUAGE_SUBTAG;
397         return nullptr;
398     }
399 
400     len = Utils::StrLen(script);
401     if (len > 0) {
402         if (LocaleMatcher::IsScriptTag(script, len) == 0) {
403             rState = INVALID_BCP47_SCRIPT_SUBTAG;
404             return nullptr;
405         }
406     }
407     len = Utils::StrLen(region);
408     if (len > 0) {
409         if (LocaleMatcher::IsRegionTag(region, len) == 0) {
410             rState = INVALID_BCP47_REGION_SUBTAG;
411             return nullptr;
412         }
413     }
414     UErrorCode errCode = U_ZERO_ERROR;
415     Locale localeInfo = icu::LocaleBuilder().setLanguage(language)
416         .setRegion(region).setScript(script).build(errCode);
417     if (!U_SUCCESS(errCode)) {
418         rState = ERROR;
419         return nullptr;
420     }
421     Locale *retLocal = new Locale(localeInfo);
422     return retLocal;
423 }
424 
GetSysDefault()425 const Locale *GetSysDefault()
426 {
427     return ResLocale::GetDefault();
428 }
429 
UpdateSysDefault(const Locale & localeInfo,bool needNotify)430 void UpdateSysDefault(const Locale &localeInfo, bool needNotify)
431 {
432     ResLocale::UpdateDefault(localeInfo, needNotify);
433 }
434 #endif
435 
FindAndSort(const std::string localeStr,std::vector<std::string> & candidateLocale,std::vector<std::string> & outValue)436 void FindAndSort(const std::string localeStr, std::vector<std::string> &candidateLocale,
437     std::vector<std::string> &outValue)
438 {
439     if (candidateLocale.size() == 0) {
440         return;
441     }
442     std::vector<ResLocale *> tempCandidate;
443     RState state = SUCCESS;
444     ResLocale *currentLocale = ResLocale::BuildFromString(localeStr.c_str(), DASH_SEP, state);
445     LocaleMatcher::Normalize(currentLocale);
446     std::vector<std::string>::const_iterator iter;
447     for (iter = candidateLocale.cbegin(); iter != candidateLocale.cend(); ++iter) {
448         ResLocale *resLocale = ResLocale::BuildFromString(iter->c_str(), DASH_SEP, state);
449         if (state == SUCCESS) {
450             LocaleMatcher::Normalize(resLocale);
451             bool isMatch = LocaleMatcher::Match(currentLocale, resLocale);
452             if (isMatch) {
453                 tempCandidate.push_back(resLocale);
454                 outValue.push_back(*iter);
455             } else {
456                 delete resLocale;
457             }
458         } else {
459             delete resLocale;
460         }
461     }
462     // sort
463     std::size_t len = tempCandidate.size();
464     if (len == 0) {
465         delete currentLocale;
466         return;
467     }
468     for (std::size_t i = 0; i < len - 1; i++) {
469         for (std::size_t j = 0; j < len - 1 - i; j++) {
470             if (LocaleMatcher::IsMoreSuitable(tempCandidate.at(j), tempCandidate.at(j + 1), currentLocale) <= 0) {
471                 ResLocale *temp = tempCandidate.at(j + 1);
472                 tempCandidate.at(j + 1) = tempCandidate.at(j);
473                 tempCandidate.at(j) = temp;
474                 std::string tempStr = outValue.at(j + 1);
475                 outValue.at(j + 1) = outValue.at(j);
476                 outValue.at(j) = tempStr;
477             }
478         }
479     }
480 
481     for (auto iter = tempCandidate.cbegin(); iter != tempCandidate.cend(); iter++) {
482         delete *iter;
483     }
484     delete currentLocale;
485 }
486 } // namespace Resource
487 } // namespace Global
488 } // namespace OHOS