• 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 "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