• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 "i18n_memory_adapter.h"
17 #include "securec.h"
18 #include "str_util.h"
19 #include "types.h"
20 #include "locale_info.h"
21 
22 static constexpr uint16_t OPT_LANG = 0x0001;
23 static constexpr uint16_t OPT_SCRIPT = 0x0002;
24 static constexpr uint16_t OPT_REGION = 0x0004;
25 static constexpr uint16_t OPT_EXTENSION = 0x0008;
26 static constexpr uint8_t TAG_COMMON = 0;
27 static constexpr uint8_t TAG_U = 1;
28 static constexpr uint8_t TAG_KEY = 2;
29 static constexpr uint8_t TAG_VALUE = 3;
30 static constexpr int LANGUAGE_MIN_LENGTH = 2;
31 static constexpr int LANGUAGE_MAX_LENGTH = 3;
32 static constexpr int REGION_LENGTH = 2;
33 static constexpr int SCRIPT_LENGTH = 4;
34 
35 using namespace OHOS::I18N;
36 
Init(const char * newLang,const char * newScript,const char * newRegion,int & status)37 void LocaleInfo::Init(const char *newLang, const char *newScript, const char *newRegion, int &status)
38 {
39     id = nullptr;
40     status = IERROR;
41     if (newLang == nullptr) {
42         return;
43     }
44     int langLength = LenCharArray(newLang);
45     // language consists of two or three letters
46     if ((langLength > LANGUAGE_MAX_LENGTH) || (langLength < LANGUAGE_MIN_LENGTH)) {
47         return;
48     }
49     I18nFree(static_cast<void *>(language));
50     language = NewArrayAndCopy(newLang, langLength);
51     if (newScript != nullptr) {
52         int scriptLength = LenCharArray(newScript);
53         if (scriptLength == SCRIPT_LENGTH) {
54             script = NewArrayAndCopy(newScript, scriptLength);
55         }
56     }
57     if (newRegion != nullptr) {
58         int regionLength = LenCharArray(newRegion);
59         if (regionLength == REGION_LENGTH) {
60             region = NewArrayAndCopy(newRegion, regionLength);
61         }
62     }
63     InitIdstr();
64     status = ISUCCESS;
65 }
66 
InitIdstr()67 void LocaleInfo::InitIdstr()
68 {
69     if (language == nullptr) {
70         return;
71     }
72     std::string idStr(language);
73     // script consists of four letters
74     if ((script != nullptr) && (LenCharArray(script) > 0)) {
75         idStr = idStr + "-" + script;
76     }
77     if ((region != nullptr) && (LenCharArray(region) > 0)) {
78         idStr = idStr + "-" + region;
79     }
80     I18nFree(static_cast<void *>(id));
81     id = NewArrayAndCopy(idStr.data(), idStr.size());
82 }
83 
LocaleInfo(const char * newLang,const char * newScript,const char * newRegion)84 LocaleInfo::LocaleInfo(const char *newLang, const char *newScript, const char *newRegion)
85 {
86     int status = ISUCCESS;
87     Init(newLang, newScript, newRegion, status);
88     if (status != ISUCCESS) {
89         SetFail();
90     }
91 }
92 
IsDefaultLocale() const93 bool LocaleInfo::IsDefaultLocale() const
94 {
95     if ((GetLanguage() == nullptr) || (GetRegion() == nullptr)) {
96         return false;
97     }
98     return ((strcmp(GetLanguage(), "en") == 0) && (strcmp(GetRegion(), "US") == 0));
99 }
100 
LocaleInfo(const char * newLang,const char * newRegion)101 LocaleInfo::LocaleInfo(const char *newLang, const char *newRegion)
102 {
103     int status = ISUCCESS;
104     Init(newLang, nullptr, newRegion, status);
105     if (status != ISUCCESS) {
106         SetFail();
107     }
108 }
109 
LocaleInfo()110 LocaleInfo::LocaleInfo()
111 {
112     id = nullptr;
113     SetFail();
114 }
115 
LocaleInfo(const LocaleInfo & o)116 LocaleInfo::LocaleInfo(const LocaleInfo &o)
117 {
118     int status = ISUCCESS;
119     Init(o.language, o.script, o.region, status);
120     if (status != ISUCCESS) {
121         SetFail();
122     }
123 }
124 
~LocaleInfo()125 LocaleInfo::~LocaleInfo()
126 {
127     FreeResource();
128 }
129 
FreeResource()130 void LocaleInfo::FreeResource()
131 {
132     I18nFree(static_cast<void *>(language));
133     I18nFree(static_cast<void *>(script));
134     I18nFree(static_cast<void *>(region));
135     I18nFree(static_cast<void *>(id));
136     I18nFree(static_cast<void *>(numberDigits));
137 }
138 
operator ==(const LocaleInfo & other) const139 bool LocaleInfo::operator ==(const LocaleInfo &other) const
140 {
141     bool ret = CompareLocaleItem(language, other.language);
142     if (!ret) {
143         return false;
144     }
145     ret = CompareLocaleItem(script, other.script);
146     if (!ret) {
147         return false;
148     }
149     ret = CompareLocaleItem(region, other.region);
150     return ret;
151 }
152 
operator =(const LocaleInfo & o)153 LocaleInfo &LocaleInfo::operator =(const LocaleInfo &o)
154 {
155     if (&o == this) {
156         return *this;
157     }
158     FreeResource();
159     if (o.language != nullptr) {
160         language = NewArrayAndCopy(o.language, strlen(o.language));
161     }
162     if (o.script != nullptr) {
163         script = NewArrayAndCopy(o.script, strlen(o.script));
164     }
165     if (o.region != nullptr) {
166         region = NewArrayAndCopy(o.region, strlen(o.region));
167     }
168     if (o.id != nullptr) {
169         id = NewArrayAndCopy(o.id, LenCharArray(o.id));
170     }
171     if (o.numberDigits != nullptr) {
172         numberDigits = NewArrayAndCopy(o.numberDigits, LenCharArray(o.numberDigits));
173     }
174     return *this;
175 }
176 
GetLanguage() const177 const char *LocaleInfo::GetLanguage() const
178 {
179     return language;
180 }
181 
GetScript() const182 const char *LocaleInfo::GetScript() const
183 {
184     return script;
185 }
186 
GetRegion() const187 const char *LocaleInfo::GetRegion() const
188 {
189     return region;
190 }
191 
GetId() const192 const char *LocaleInfo::GetId() const
193 {
194     const char *rid = id;
195     return rid;
196 }
197 
IsSuccess()198 bool LocaleInfo::IsSuccess()
199 {
200     bool r = isSucc;
201     isSucc = true;
202     return r;
203 }
204 
SetFail()205 void LocaleInfo::SetFail()
206 {
207     isSucc = false;
208 }
209 
ChangeLanguageCode(char * lang,const int32_t dstSize,const char * src,const int32_t srcSize) const210 bool LocaleInfo::ChangeLanguageCode(char *lang, const int32_t dstSize, const char *src, const int32_t srcSize) const
211 {
212     if (lang == nullptr || src == nullptr) {
213         return false;
214     }
215     if (srcSize == (LANGUAGE_MIN_LENGTH + 1)) { // three letter language only support fil and mai
216         if (memcmp(src, "fil", srcSize) == 0) {
217             lang[0] = 't';
218             lang[1] = 'l';
219         } else if (memcmp(src, "mai", srcSize) == 0) {
220             lang[0] = 'm';
221             lang[1] = 'd';
222         } else {
223             return false;
224         }
225         return true;
226     } else if (srcSize == LANGUAGE_MIN_LENGTH) {
227         if (memcmp(src, "he", srcSize) == 0) {
228             lang[0] = 'i';
229             lang[1] = 'w';
230         } else if (memcmp(src, "id", srcSize) == 0) {
231             lang[0] = 'i';
232             lang[1] = 'n';
233         } else {
234             if (strcpy_s(lang, dstSize, src) != EOK) {
235                 return false;
236             }
237         }
238         return true;
239     }
240     return false;
241 }
242 
GetMask() const243 uint32_t LocaleInfo::GetMask() const
244 {
245     if (language == nullptr) {
246         return 0;
247     }
248     char lang[LANGUAGE_MAX_LENGTH];
249     bool isRight = ChangeLanguageCode(lang, LANGUAGE_MAX_LENGTH, language, LenCharArray(language));
250     if (!isRight) {
251         return 0;
252     }
253     // use 7bit to represent an English letter,
254     // 32--- language ---18--- script ---14--- region ---0
255     uint32_t tempLangFirst = (lang[0] - CHAR_OFF);
256     uint32_t tempLangSecond = (lang[1] - CHAR_OFF);
257     uint32_t mask = (tempLangFirst << LANG_FIRST_BEGIN) | (tempLangSecond << LANG_SECOND_BEGIN);
258     if ((script != nullptr) && (LenCharArray(script) > 0)) {
259         if (strcmp(script, "Hans") == 0) {
260             mask = mask | (HANS << SCRIPT_BEGIN);
261         } else if (strcmp(script, "Hant") == 0) {
262             mask = mask | (HANT << SCRIPT_BEGIN);
263         } else if (strcmp(script, "Latn") == 0) {
264             mask = mask | (LATN << SCRIPT_BEGIN);
265         } else if (strcmp(script, "Qaag") == 0) {
266             mask = mask | (QAAG << SCRIPT_BEGIN);
267         } else if (strcmp(script, "Cyrl") == 0) {
268             mask = mask | (CYRL << SCRIPT_BEGIN);
269         } else if (strcmp(script, "Deva") == 0) {
270             mask = mask | (DEVA << SCRIPT_BEGIN);
271         } else if (strcmp(script, "Guru") == 0) {
272             mask = mask | (GURU << SCRIPT_BEGIN);
273         }
274     }
275     if ((region != nullptr) && (LenCharArray(region) > 1)) {
276         uint32_t tempRegion = (region[0] - CHAR_OFF);
277         uint32_t tempRegionSecond = (region[1] - CHAR_OFF);
278         mask = mask | (tempRegion << REGION_FIRST_LETTER) | (tempRegionSecond);
279     }
280     return mask;
281 }
282 
ForLanguageTag(const char * languageTag,I18nStatus & status)283 LocaleInfo LocaleInfo::ForLanguageTag(const char *languageTag, I18nStatus &status)
284 {
285     LocaleInfo locale;
286     if (languageTag == nullptr) {
287         status = IERROR;
288         return locale;
289     }
290     ParseLanguageTag(locale, languageTag, status);
291     locale.InitIdstr();
292     return locale;
293 }
294 
ParseLanguageTag(LocaleInfo & locale,const char * languageTag,I18nStatus & status)295 void LocaleInfo::ParseLanguageTag(LocaleInfo &locale, const char *languageTag, I18nStatus &status)
296 {
297     const char *tag = languageTag;
298     uint16_t options = OPT_LANG;
299     const char *key = nullptr;
300     const char *value = nullptr;
301     uint8_t type = 0;
302     while (tag) {
303         const char *start = tag;
304         const char *end = tag;
305         while (*end) {
306             if (*end == '-') {
307                 break;
308             }
309             ++end;
310         }
311         tag = end + 1;
312         if (*end == '\0') {
313             tag = nullptr;
314         }
315         auto tagLength = end - start;
316         ConfirmTagType(start, tagLength, type, key, value);
317         if (!ParseNormalSubTag(locale, start, tagLength, options, type)) {
318             if ((options & OPT_EXTENSION) && (type == TAG_VALUE)) {
319                 ProcessExtension(locale, key, value);
320                 type = TAG_COMMON;
321             }
322         }
323     }
324     I18nFree(static_cast<void *>(const_cast<char *>(key)));
325     I18nFree(static_cast<void *>(const_cast<char *>(value)));
326 }
327 
ParseNormalSubTag(LocaleInfo & locale,const char * start,size_t tagLength,uint16_t & options,uint8_t & type)328 bool LocaleInfo::ParseNormalSubTag(LocaleInfo &locale, const char *start, size_t tagLength, uint16_t &options,
329     uint8_t &type)
330 {
331     if ((start == nullptr) || (tagLength == 0)) {
332         return false;
333     }
334     if ((options & OPT_LANG) && (type == TAG_COMMON)) {
335         if (IsLanguage(start, tagLength)) {
336             locale.language = I18nNewCharString(start, tagLength);
337             options &= ~OPT_LANG;
338             options |= OPT_SCRIPT | OPT_REGION | OPT_EXTENSION;
339             return true;
340         }
341     }
342     if ((options & OPT_SCRIPT) && (type == TAG_COMMON)) {
343         if (IsScript(start, tagLength)) {
344             options &= ~OPT_SCRIPT;
345             locale.script = I18nNewCharString(start, tagLength);
346             return true;
347         }
348     }
349     if ((options & OPT_REGION) && (type == TAG_COMMON)) {
350         if (IsRegion(start, tagLength)) {
351             options &= ~OPT_REGION;
352             options &= ~OPT_SCRIPT;
353             locale.region = I18nNewCharString(start, tagLength);
354             return true;
355         }
356     }
357     return false;
358 }
359 
ConfirmTagType(const char * start,size_t length,uint8_t & type,const char * & key,const char * & value)360 void LocaleInfo::ConfirmTagType(const char *start, size_t length, uint8_t &type, const char* &key, const char* &value)
361 {
362     if (start == nullptr) {
363         return;
364     }
365     switch (type) {
366         case TAG_COMMON: {
367             if ((length == 1) && (*start == 'u')) {
368                 type = TAG_U;
369             }
370             return;
371         }
372         case TAG_U: {
373             type = TAG_KEY;
374             I18nFree(static_cast<void *>(const_cast<char *>(key)));
375             key = I18nNewCharString(start, length);
376             return;
377         }
378         case TAG_KEY: {
379             type = TAG_VALUE;
380             I18nFree(static_cast<void *>(const_cast<char *>(value)));
381             value = I18nNewCharString(start, length);
382             return;
383         }
384         default: {
385             type = TAG_COMMON;
386             return;
387         }
388     }
389 }
390 
ProcessExtension(LocaleInfo & locale,const char * key,const char * value)391 void LocaleInfo::ProcessExtension(LocaleInfo &locale, const char *key, const char *value)
392 {
393     if (key == nullptr || value == nullptr) {
394         return;
395     }
396     // now we only support numbering systems in extensions
397     if (strcmp(key, "nu") == 0) {
398         locale.numberDigits = NewArrayAndCopy(value, strlen(value));
399         return;
400     }
401 }
402 
IsLanguage(const char * start,uint8_t length)403 bool LocaleInfo::IsLanguage(const char *start, uint8_t length)
404 {
405     if ((length != LANGUAGE_MAX_LENGTH) && (length != LANGUAGE_MIN_LENGTH)) {
406         return false;
407     }
408     for (uint8_t i = 0; i < length; ++i) {
409         const char ch = *(start + i);
410         if (ch < 'a' || ch > 'z') {
411             return false;
412         }
413     }
414     return true;
415 }
416 
IsScript(const char * start,uint8_t length)417 bool LocaleInfo::IsScript(const char *start, uint8_t length)
418 {
419     // all scripts's length is 4,
420     // now we support Latn, Hans, Hant, Qaag, Cyrl, Deva, Guru
421     if (length != SCRIPT_LENGTH || start == nullptr) {
422         return false;
423     }
424     if (memcmp(start, "Hans", length) == 0) {
425         return true;
426     } else if (memcmp(start, "Latn", length) == 0) {
427         return true;
428     } else if (memcmp(start, "Hant", length) == 0) {
429         return true;
430     } else if (memcmp(start, "Qaag", length) == 0) {
431         return true;
432     } else if (memcmp(start, "Cyrl", length) == 0) {
433         return true;
434     } else if (memcmp(start, "Deva", length) == 0) {
435         return true;
436     } else if (memcmp(start, "Guru", length) == 0) {
437         return true;
438     } else {
439         return false;
440     }
441 }
442 
IsRegion(const char * start,uint8_t length)443 bool LocaleInfo::IsRegion(const char *start, uint8_t length)
444 {
445     if (length != REGION_LENGTH) {
446         return false;
447     }
448     for (uint8_t i = 0; i < length; ++i) {
449         const char ch = *(start + i);
450         if (ch < 'A' || ch > 'Z') { // region characters should all be upper case.
451             return false;
452         }
453     }
454     return true;
455 }
456 
GetExtension(const char * key)457 const char *LocaleInfo::GetExtension(const char *key)
458 {
459     if (strcmp(key, "nu") == 0) {
460         return numberDigits;
461     }
462     return nullptr;
463 }
464