• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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