• 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 #include "locale_matcher.h"
16 
17 #include <cstring>
18 #include <new>
19 #include "likely_subtags_key_data.cpp"
20 #include "likely_subtags_value_data.cpp"
21 
22 #include "utils/common.h"
23 #include "utils/locale_data.h"
24 #include "utils/utils.h"
25 
26 namespace OHOS {
27 namespace Global {
28 namespace Resource {
29 // Special locale constant
30 uint64_t LocaleMatcher::EN_US_ENCODE = Utils::EncodeLocale("en", nullptr, "US");
31 uint64_t LocaleMatcher::EN_GB_ENCODE = Utils::EncodeLocale("en", nullptr, "GB");
32 uint64_t LocaleMatcher::EN_QAAG_ENCODE = Utils::EncodeLocale("en", "Qaag", nullptr);
33 uint64_t LocaleMatcher::ZH_HANT_MO_ENCODE = Utils::EncodeLocale("zh", "Hant", "MO");
34 uint64_t LocaleMatcher::ZH_HK_ENCODE = Utils::EncodeLocale("zh", nullptr, "HK");
35 uint32_t LocaleMatcher::HANT_ENCODE = Utils::EncodeScript("Hant");
36 
IsContainRegion(uint64_t encodedLocale)37 bool IsContainRegion(uint64_t encodedLocale)
38 {
39     return (encodedLocale & 0x000000000000FFFFLU) != 0;
40 }
41 
ClearRegion(uint64_t encodedLocale)42 uint64_t ClearRegion(uint64_t encodedLocale)
43 {
44     return encodedLocale & 0xFFFFFFFFFFFF0000LU;
45 }
46 
AddScript(uint64_t encodedLocale,uint32_t encodedScript)47 uint64_t AddScript(uint64_t encodedLocale, uint32_t encodedScript)
48 {
49     return  (encodedLocale | ((static_cast<uint64_t>(encodedScript) & 0x00000000FFFFFFFFLU) << 16));
50 }
51 
52 /**
53  * @brief Find region parent locale, if locale has not contain region, return ROOT_LOCALE.
54  * Lookup child-parent locale tables, if tables has not contains, then clear the locale region and return it.
55  *
56  * @param encodedLocale locale encode
57  * @return uint64_t  parent locale encode
58  */
SearchParentLocale(uint64_t encodedLocale,const ResLocale * request)59 uint64_t SearchParentLocale(uint64_t encodedLocale, const ResLocale *request)
60 {
61     uint64_t tempEncodedLocale = encodedLocale;
62     if (Utils::EncodeScriptByResLocale(request) == LocaleMatcher::HANT_ENCODE) {
63         tempEncodedLocale = AddScript(encodedLocale, LocaleMatcher::HANT_ENCODE);
64         if (tempEncodedLocale == LocaleMatcher::ZH_HANT_MO_ENCODE) {
65             return LocaleMatcher::ZH_HK_ENCODE;
66         }
67     }
68     if (IsContainRegion(encodedLocale)) {
69         size_t len = sizeof(LOCALE_PARENTS_KEY) / sizeof(LOCALE_PARENTS_KEY[0]);
70         for (size_t i = 0; i < len; i++) {
71             if (LOCALE_PARENTS_KEY[i] == tempEncodedLocale) {
72                 return LOCALE_PARENTS_VALUE[i];
73             }
74         }
75         return ClearRegion(encodedLocale);
76     }
77     return LocaleMatcher::ROOT_LOCALE;
78 }
79 
80 /**
81  * @brief find locale ancestors, including itself and ROOT_LOCALE.
82  *
83  * @param result
84  * @param len
85  * @param encodedLocale
86  */
FindTrackPath(const ResLocale * request,size_t len,uint64_t encodedLocale,uint64_t * result)87 void FindTrackPath(const ResLocale *request, size_t len, uint64_t encodedLocale, uint64_t *result)
88 {
89     uint64_t currentEncodedLocale = encodedLocale;
90     size_t i = 0;
91     do {
92         result[i] = currentEncodedLocale;
93         currentEncodedLocale = SearchParentLocale(currentEncodedLocale, request);
94         ++i;
95     } while (currentEncodedLocale != LocaleMatcher::ROOT_LOCALE);
96     if (i < len) {
97         result[i] = LocaleMatcher::ROOT_LOCALE;
98     }
99 }
100 
101 /**
102  * @brief find encodedLocale pos is in trackpath list.
103  *
104  * @param paths
105  * @param len
106  * @param encodedLocale
107  * @return int8_t
108  */
SearchTrackPathDistance(const uint64_t * paths,size_t len,uint64_t encodedLocale)109 int8_t SearchTrackPathDistance(const uint64_t *paths, size_t len, uint64_t encodedLocale)
110 {
111     size_t i = 0;
112     for (i = 0; i < len; ++i) {
113         if (paths[i] == LocaleMatcher::ROOT_LOCALE) {
114             return i;
115         }
116         if (paths[i] == encodedLocale) {
117             return i;
118         }
119     }
120     return static_cast<int8_t>(i);
121 }
122 
123 /**
124  * @brief find locale is in typical locale.
125  *
126  * @param language
127  * @param script
128  * @param region
129  * @return true
130  * @return false
131  */
IsDefaultLocale(const char * language,const char * script,const char * region)132 bool IsDefaultLocale(const char *language, const char *script, const char *region)
133 {
134     uint64_t encodedLocale = Utils::EncodeLocale(language, script, region);
135     if (ClearRegion(encodedLocale) == LocaleMatcher::EN_QAAG_ENCODE) {
136         encodedLocale = Utils::EncodeLocale("en", "Latn", region);
137     }
138     size_t len = sizeof(TYPICAL_CODES_VALUE) / sizeof(TYPICAL_CODES_VALUE[0]);
139     for (size_t i = 0; i < len; i++) {
140         if (TYPICAL_CODES_VALUE[i] == encodedLocale) {
141             return true;
142         }
143     }
144     return false;
145 }
146 
147 /**
148  * @brief find the default script of language and region.
149  * first search language and region corresponding  script.
150  * if not found,search language corresponding script.
151  *
152  * @param language
153  * @param region
154  * @return uint32_t
155  */
FindDefaultScriptEncode(const char * language,const char * region)156 uint32_t FindDefaultScriptEncode(const char *language, const char *region)
157 {
158     uint64_t encodedLocale = Utils::EncodeLocale(language, nullptr, region);
159     size_t len = sizeof(LIKELY_TAGS_CODES_KEY) / sizeof(LIKELY_TAGS_CODES_KEY[0]);
160     for (size_t i = 0; i < len; i++) {
161         if (LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
162             return static_cast<uint32_t>((LIKELY_TAGS_CODES_VALUE[i] & 0x0000ffffffff0000) >> 16);
163         }
164     }
165     if (region != nullptr) {
166         encodedLocale = Utils::EncodeLocale(language, nullptr, nullptr);
167         for (size_t i = 0; i < len; i++) {
168             if (LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
169                 return static_cast<uint32_t>((LIKELY_TAGS_CODES_VALUE[i] & 0x0000ffffffff0000) >> 16);
170             }
171         }
172     }
173     return LocaleMatcher::NULL_SCRIPT;
174 }
175 
176 /**
177  * @brief find the default region of language and script.
178  * first search language and script corresponding region.
179  * if not found,search language corresponding region.
180  *
181  * @param language
182  * @param script
183  * @return uint16_t
184  */
FindDefaultRegionEncode(const char * language,const char * script)185 uint16_t FindDefaultRegionEncode(const char *language, const char *script)
186 {
187     /* first try language and script */
188     uint64_t encodedLocale = Utils::EncodeLocale(language, script, nullptr);
189     if (encodedLocale == Utils::EncodeLocale("en", "Qaag", nullptr)) {
190         encodedLocale = Utils::EncodeLocale("en", "Latn", nullptr);
191     }
192     size_t len = sizeof(LIKELY_TAGS_CODES_KEY) / sizeof(LIKELY_TAGS_CODES_KEY[0]);
193     for (size_t i = 0; i < len; i++) {
194         if (LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
195             return static_cast<uint16_t>((LIKELY_TAGS_CODES_VALUE[i] & 0x000000000000ffff));
196         }
197     }
198     /* if not found and script is not null,try language */
199     if (script != nullptr) {
200         encodedLocale = Utils::EncodeLocale(language, nullptr, nullptr);
201         for (size_t i = 0; i < len; i++) {
202             if (LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
203                 return static_cast<uint16_t>((LIKELY_TAGS_CODES_VALUE[i] & 0x000000000000ffff));
204             }
205         }
206     }
207     return LocaleMatcher::NULL_REGION;
208 };
209 
210 /**
211  * @brief find the  first locale which in target path,is also in request path.
212  * return sum of the locale pos in target path and request path.
213  *
214  * @param requestPaths
215  * @param targetPaths
216  * @param len
217  * @return size_t
218  */
ComputeTrackPathDistance(const uint64_t * requestPaths,const uint64_t * targetPaths,size_t len)219 size_t ComputeTrackPathDistance(const uint64_t *requestPaths,
220                                 const uint64_t *targetPaths, size_t len)
221 {
222     size_t i = 0;
223     size_t j = 0;
224     for (i = 0; i < len; ++i) {
225         if (targetPaths[i] == LocaleMatcher::ROOT_LOCALE) {
226             // targetpath not in request path,so distance is 2*len
227             return len * 2;
228         }
229         for (j = 0; j < len; ++j) {
230             if (requestPaths[j] == targetPaths[i]) {
231                 return i + j;
232             }
233         }
234     }
235     return   len * 2;
236 }
237 
CompareRegionWhenQaag(const ResLocale * current,const ResLocale * other,const ResLocale * request)238 int8_t CompareRegionWhenQaag(const ResLocale *current,
239     const ResLocale *other,
240     const ResLocale *request)
241 {
242     if ((request != nullptr) && (Utils::EncodeLocale(request->GetLanguage(), request->GetScript(),
243         nullptr) == LocaleMatcher::EN_QAAG_ENCODE)) {
244         if ((current != nullptr) && (Utils::EncodeLocale(current->GetLanguage(), nullptr,
245             current->GetRegion()) == LocaleMatcher::EN_GB_ENCODE)) {
246             return 1;
247         }
248         if ((other != nullptr) && (Utils::EncodeLocale(other->GetLanguage(), nullptr, other->GetRegion()) ==
249             LocaleMatcher::EN_GB_ENCODE)) {
250             return -1;
251         }
252     }
253     return 0;
254 }
255 
256 /**
257  * @brief compare language,support new/old language code
258  * NEW_LANGUAGES_CODES is new language code,
259  * OLD_LANGUAGES_CODES is old language code.
260  * support iw/he,tl/fil,ji/yi,jw/jv,in/id.
261  * @param current
262  * @param other
263  * @return true
264  * @return false
265  */
CompareLanguage(const ResLocale * current,const ResLocale * other)266 bool CompareLanguage(const ResLocale *current, const ResLocale *other)
267 {
268     uint16_t currentEncodedLanguage =
269         Utils::EncodeLanguageByResLocale(current);
270     uint16_t otherEncodedLanguage = Utils::EncodeLanguageByResLocale(
271         other);
272     return ((currentEncodedLanguage == otherEncodedLanguage) ||
273         ((currentEncodedLanguage == NEW_LANGUAGES_CODES[0])
274             && (otherEncodedLanguage == OLD_LANGUAGES_CODES[0])) ||
275         ((otherEncodedLanguage == NEW_LANGUAGES_CODES[0])
276             && (currentEncodedLanguage == OLD_LANGUAGES_CODES[0])) ||
277         ((currentEncodedLanguage == NEW_LANGUAGES_CODES[1])
278             && (otherEncodedLanguage == OLD_LANGUAGES_CODES[1])) ||
279         ((otherEncodedLanguage == NEW_LANGUAGES_CODES[1])
280             && (currentEncodedLanguage == OLD_LANGUAGES_CODES[1])) ||
281         ((currentEncodedLanguage == NEW_LANGUAGES_CODES[2])
282             && (otherEncodedLanguage == OLD_LANGUAGES_CODES[2])) ||
283         ((otherEncodedLanguage == NEW_LANGUAGES_CODES[2])
284             && (currentEncodedLanguage == OLD_LANGUAGES_CODES[2])) ||
285         ((currentEncodedLanguage == NEW_LANGUAGES_CODES[3])
286             && (otherEncodedLanguage == OLD_LANGUAGES_CODES[3])) ||
287         ((otherEncodedLanguage == NEW_LANGUAGES_CODES[3])
288             && (currentEncodedLanguage == OLD_LANGUAGES_CODES[3])) ||
289         ((currentEncodedLanguage == NEW_LANGUAGES_CODES[4])
290             && (otherEncodedLanguage == OLD_LANGUAGES_CODES[4])) ||
291         ((otherEncodedLanguage == NEW_LANGUAGES_CODES[4])
292             && (currentEncodedLanguage == OLD_LANGUAGES_CODES[4])));
293 }
294 
CompareScript(const ResLocale * current,const ResLocale * other)295 bool CompareScript(const ResLocale *current, const ResLocale *other)
296 {
297     uint32_t currentEncodedScript = 0;
298     uint32_t otherEncodedScript = 0;
299     if ((current != nullptr) && (current->GetScript() == nullptr)) {
300         currentEncodedScript = FindDefaultScriptEncode(current->GetLanguage(),
301             current->GetRegion());
302     } else {
303         currentEncodedScript = Utils::EncodeScriptByResLocale(current);
304     }
305     if ((other != nullptr) && (other->GetScript() == nullptr)) {
306         otherEncodedScript = FindDefaultScriptEncode(other->GetLanguage(),
307             other->GetRegion());
308     } else {
309         otherEncodedScript = Utils::EncodeScriptByResLocale(other);
310     }
311     if (current != nullptr && other != nullptr) {
312         // when current locale is en-Qaag is equal en-Latn
313         if (Utils::EncodeLocale(current->GetLanguage(), current->GetScript(), nullptr) ==
314             Utils::EncodeLocale("en", "Qaag", nullptr)) {
315             if (Utils::EncodeLocale(other->GetLanguage(), other->GetScript(), nullptr) ==
316                 Utils::EncodeLocale("en", "Latn", nullptr)) {
317                 return true;
318             }
319         }
320     }
321     bool compareRegion = false;
322     if ((currentEncodedScript == LocaleMatcher::NULL_SCRIPT) || (otherEncodedScript == LocaleMatcher::NULL_SCRIPT)) {
323         // if request script is null, region must be same
324         compareRegion = true;
325     }
326     if (compareRegion) {
327         uint16_t currentRegionEncode = Utils::EncodeRegionByResLocale(current);
328         uint16_t otherRegionEncode = Utils::EncodeRegionByResLocale(other);
329         return (otherRegionEncode == LocaleMatcher::NULL_REGION) || (currentRegionEncode == otherRegionEncode);
330     }
331     return currentEncodedScript == otherEncodedScript;
332 }
333 
AlphabeticallyCompare(const ResLocale * current,uint64_t currentEncodedLocale,const ResLocale * other,uint64_t otherEncodedLocale)334 int8_t AlphabeticallyCompare(const ResLocale *current,
335     uint64_t currentEncodedLocale,
336     const ResLocale *other,
337     uint64_t otherEncodedLocale)
338 {
339     if (currentEncodedLocale == otherEncodedLocale) {
340         return 0;
341     }
342     if (current == nullptr || current->GetRegion() == nullptr) {
343         return -1;
344     }
345     if (other == nullptr || other->GetRegion() == nullptr) {
346         return 1;
347     }
348     // be here region is not null
349     char currentFirstChar = (current->GetRegion())[0];
350     char otherFirstChar = (other->GetRegion())[0];
351     if (currentFirstChar >= '0' && currentFirstChar <= '9') {
352         if (otherFirstChar < '0' || otherFirstChar > '9') {
353             return -1;
354         }
355     } else {
356         if (otherFirstChar >= '0' && otherFirstChar <= '9') {
357             return 1;
358         }
359     }
360     if (currentEncodedLocale > otherEncodedLocale) {
361         return -1;
362     }
363     if (otherEncodedLocale > currentEncodedLocale) {
364         return 1;
365     }
366     return 0;
367 }
368 
CompareWhenRegionIsNull(uint16_t currentEncodedRegion,uint16_t otherEncodedRegion,const ResLocale * current,const ResLocale * other,const ResLocale * request)369 int8_t CompareWhenRegionIsNull(uint16_t currentEncodedRegion, uint16_t otherEncodedRegion,
370     const ResLocale *current,
371     const ResLocale *other,
372     const ResLocale *request)
373 {
374     if (current == nullptr || current->GetRegion() == nullptr) {
375         return 1;
376     }
377     if (other == nullptr || other->GetRegion() == nullptr) {
378         return -1;
379     }
380     int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
381     if (qaagResult != 0) {
382         return qaagResult;
383     }
384     // get request default region
385     uint16_t requestDefaultRegion =
386         FindDefaultRegionEncode((request == nullptr) ? nullptr : request->GetLanguage(),
387             (request == nullptr) ? nullptr : request->GetScript());
388     if (requestDefaultRegion == currentEncodedRegion) {
389         return 1;
390     }
391     if (requestDefaultRegion == otherEncodedRegion) {
392         return -1;
393     }
394     // current and other region is not null.alphabetically
395     uint64_t currentEncodedLocale = Utils::EncodeLocale((request == nullptr) ? nullptr : request->GetLanguage(),
396         nullptr, (current == nullptr) ? nullptr : current->GetRegion());
397     uint64_t otherEncodedLocale = Utils::EncodeLocale(
398         (request == nullptr) ? nullptr : request->GetLanguage(), nullptr, other->GetRegion());
399     return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
400 }
401 
CompareDistance(uint64_t currentEncodedLocale,uint64_t otherEncodedLocale,const uint64_t * requestEncodedTrackPath,const ResLocale * request)402 int8_t CompareDistance(uint64_t currentEncodedLocale, uint64_t otherEncodedLocale,
403     const uint64_t *requestEncodedTrackPath, const ResLocale *request)
404 {
405     uint64_t currentEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
406     FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, currentEncodedLocale, currentEncodedTrackPath);
407     uint64_t otherEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
408     FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, otherEncodedLocale, otherEncodedTrackPath);
409     const size_t currentDistance = ComputeTrackPathDistance(
410         requestEncodedTrackPath, currentEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE);
411     const size_t targetDistance = ComputeTrackPathDistance(
412         requestEncodedTrackPath, otherEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE);
413     if (currentDistance < targetDistance) {
414         return 1;
415     }
416     if (currentDistance > targetDistance) {
417         return -1;
418     }
419     return 0;
420 }
421 
CompareDefaultRegion(const ResLocale * current,const ResLocale * other,const ResLocale * request)422 int8_t CompareDefaultRegion(const ResLocale *current,
423     const ResLocale *other,
424     const ResLocale *request)
425 {
426     int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
427     if (qaagResult != 0) {
428         return qaagResult;
429     } else {
430         bool isCurrentDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage(),
431             (request == nullptr) ? nullptr : request->GetScript(),
432             (current == nullptr) ? nullptr : current->GetRegion());
433         bool isOtherDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage(),
434             (request == nullptr) ? nullptr : request->GetScript(), (other == nullptr) ? nullptr : other->GetRegion());
435         if (isCurrentDefaultRegion != isOtherDefaultRegion) {
436             if (isCurrentDefaultRegion) {
437                 return 1;
438             } else {
439                 return -1;
440             }
441         }
442     }
443     return 0;
444 }
445 
446 /**
447  * @brief compare current and target region, which is better for request.
448  * @param current current locale
449  * @param target target locale
450  * @param request request locale
451  * @return int8_t if current region is better than target region,return 1. if current region is equal target region,
452  *         return 0. If target region is better than current region, return -1.
453  */
CompareRegion(const ResLocale * current,const ResLocale * other,const ResLocale * request)454 int8_t CompareRegion(const ResLocale *current,
455                      const ResLocale *other,
456                      const ResLocale *request)
457 {
458     uint16_t currentEncodedRegion = Utils::EncodeRegionByResLocale(current);
459     uint16_t otherEncodedRegion = Utils::EncodeRegionByResLocale(other);
460     if (request == nullptr || request->GetRegion() == nullptr) {
461         return CompareWhenRegionIsNull(currentEncodedRegion, otherEncodedRegion, current, other, request);
462     }
463     uint64_t requestEncodedLocale = Utils::EncodeLocale(
464         request->GetLanguage(), nullptr, request->GetRegion());
465     uint64_t requestEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
466     FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, requestEncodedLocale, requestEncodedTrackPath);
467     uint64_t currentEncodedLocale = Utils::EncodeLocale(
468         request->GetLanguage(), nullptr, (current == nullptr) ? nullptr : current->GetRegion());
469     uint64_t otherEncodedLocale = Utils::EncodeLocale(
470         request->GetLanguage(), nullptr, (other == nullptr) ? nullptr : other->GetRegion());
471     int8_t currentMatchDistance = SearchTrackPathDistance(
472         requestEncodedTrackPath,
473         LocaleMatcher::TRACKPATH_ARRAY_SIZE,
474         currentEncodedLocale);
475     int8_t otherMatchDistance = SearchTrackPathDistance(
476         requestEncodedTrackPath,
477         LocaleMatcher::TRACKPATH_ARRAY_SIZE,
478         otherEncodedLocale);
479     if (currentMatchDistance < otherMatchDistance) {
480         return 1;
481     }
482     if (currentMatchDistance > otherMatchDistance) {
483         return -1;
484     }
485     int8_t result = CompareDistance(currentEncodedLocale, otherEncodedLocale, requestEncodedTrackPath, request);
486     if (result != 0) {
487         return result;
488     }
489     result = CompareDefaultRegion(current, other, request);
490     if (result != 0) {
491         return result;
492     }
493     uint16_t requestDefaultRegion =
494         FindDefaultRegionEncode(request->GetLanguage(), request->GetScript());
495     if (requestDefaultRegion == currentEncodedRegion) {
496         return 1;
497     }
498     if (requestDefaultRegion == otherEncodedRegion) {
499         return -1;
500     }
501     return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
502 }
503 
Match(const ResLocale * current,const ResLocale * other)504 bool LocaleMatcher::Match(const ResLocale *current, const ResLocale *other)
505 {
506     if (current == nullptr || other == nullptr) {
507         return true;
508     }
509     // language is not null.
510     bool isLanguageEqual = CompareLanguage(current, other);
511     if (!isLanguageEqual) {
512         return false;
513     }
514     return CompareScript(current, other);
515 };
516 
Normalize(ResLocale * localeInfo)517 bool LocaleMatcher::Normalize(ResLocale *localeInfo)
518 {
519     if (localeInfo == nullptr) {
520         return true;
521     }
522     if (!Utils::IsStrEmpty(localeInfo->GetScript())) {
523         return true;
524     }
525     uint32_t encodedScript = FindDefaultScriptEncode(localeInfo->GetLanguage(),
526                                                      localeInfo->GetRegion());
527     if (encodedScript == LocaleMatcher::NULL_SCRIPT) {
528         return true;
529     }
530     char *tempScript = new(std::nothrow) char[SCRIPT_ARRAY_LEN];
531     if (tempScript == nullptr) {
532         return false;
533     }
534     tempScript[SCRIPT_LEN] = '\0';
535     Utils::DecodeScript(encodedScript, tempScript);
536     localeInfo->script_ = tempScript;
537     return true;
538 }
539 
CompareLanguageIgnoreOldNewCode(const ResLocale * current,const ResLocale * other,const ResLocale * request)540 int8_t CompareLanguageIgnoreOldNewCode(const ResLocale *current, const ResLocale *other, const ResLocale *request)
541 {
542     uint16_t currentLanguageEncode = Utils::EncodeLanguageByResLocale(current);
543     uint16_t otherLanguageEncode = Utils::EncodeLanguageByResLocale(other);
544     uint16_t requestLanguageEncode = Utils::EncodeLanguageByResLocale(request);
545     if ((currentLanguageEncode == requestLanguageEncode) && (otherLanguageEncode != requestLanguageEncode)) {
546         return 1;
547     }
548     if ((otherLanguageEncode == requestLanguageEncode) && (currentLanguageEncode != requestLanguageEncode)) {
549         return -1;
550     }
551     return 0;
552 }
553 
IsSimilarToUsEnglish(const ResLocale * localeInfo)554 bool IsSimilarToUsEnglish(const ResLocale *localeInfo)
555 {
556     uint64_t localeEncode = Utils::EncodeLocale("en", nullptr,
557         (localeInfo == nullptr) ? nullptr : localeInfo->GetRegion());
558     uint64_t loclaeEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
559     FindTrackPath(nullptr, LocaleMatcher::TRACKPATH_ARRAY_SIZE, localeEncode, loclaeEncodedTrackPath);
560     uint8_t len = LocaleMatcher::TRACKPATH_ARRAY_SIZE;
561     for (uint8_t i = 0; i < len; ++i) {
562         if (loclaeEncodedTrackPath[i] == Utils::EncodeLocale("en", nullptr, nullptr)) {
563             return true;
564         }
565         if (loclaeEncodedTrackPath[i] == Utils::EncodeLocale("en", nullptr, "001")) {
566             return false;
567         }
568     }
569     return   false;
570 }
CompareRegionWhenLangIsNotEqual(const ResLocale * current,const ResLocale * other,const ResLocale * request)571 bool CompareRegionWhenLangIsNotEqual(const ResLocale *current,
572     const ResLocale *other,
573     const ResLocale *request)
574 {
575     int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
576     if (qaagResult != 0) {
577         return qaagResult;
578     }
579     if (request != nullptr && (Utils::EncodeLanguage(request->GetLanguage())) == Utils::EncodeLanguage("en")) {
580         // when request is en-us,empty region is better
581         if ((Utils::EncodeRegion(request->GetRegion())) == Utils::EncodeRegion("US")) {
582             if (current != nullptr) {
583                 return (current->GetRegion() == nullptr) ||
584                             ((Utils::EncodeRegion(current->GetRegion())) == Utils::EncodeRegion("US"));
585             } else {
586                 return !(other->GetRegion() == nullptr ||
587                             ((Utils::EncodeRegion(other->GetRegion())) == Utils::EncodeRegion("US")));
588             }
589         } else if (IsSimilarToUsEnglish(request)) {
590             if (current != nullptr) {
591                 return IsSimilarToUsEnglish(current);
592             } else {
593                 return !IsSimilarToUsEnglish(other);
594             }
595         }
596     }
597     return current != nullptr;
598 }
599 
IsMoreSuitable(const ResLocale * current,const ResLocale * other,const ResLocale * request)600 int8_t LocaleMatcher::IsMoreSuitable(const ResLocale *current,
601     const ResLocale *other,
602     const ResLocale *request)
603 {
604     if (request == nullptr) {
605         // if request ResLocale is nullptr, the candidate is also nullptr will be more suitable
606         if (current != nullptr && other == nullptr) {
607         // -1 means other is more suitable
608             return -1;
609         }
610         if (current == nullptr && other != nullptr) {
611             // 1 means current is more suitable
612             return 1;
613         }
614         return 0;
615     }
616     if (current == nullptr && other == nullptr) {
617         return 0;
618     }
619     bool isLangEqual = CompareLanguage(current, other);
620     if (!isLangEqual) {
621         // current or other language is null, not null language is better
622         bool result = CompareRegionWhenLangIsNotEqual(current, other, request);
623         return result ? 1 : -1;
624     }
625     uint16_t currentEncodedRegion =
626         Utils::EncodeRegionByResLocale(current);
627     uint16_t otherEncodedRegion =
628         Utils::EncodeRegionByResLocale(other);
629     if (currentEncodedRegion == otherEncodedRegion) {
630         // same language,same script,same region
631         return CompareLanguageIgnoreOldNewCode(current, other, request);
632     }
633     // equal request region is better
634     uint16_t requestEncodedRegion = Utils::EncodeRegionByResLocale(request);
635     if (currentEncodedRegion == requestEncodedRegion) {
636         return 1;
637     }
638     if (otherEncodedRegion == requestEncodedRegion) {
639         return -1;
640     }
641     int8_t isRegionEqual = CompareRegion(current, other, request);
642     if (isRegionEqual == 0) {
643         return CompareLanguageIgnoreOldNewCode(current, other, request);
644     }
645     return isRegionEqual;
646 };
647 
648 /**
649  * @brief language tag is 2 or 3 letters
650  *
651  * @param str
652  * @param len
653  * @return true
654  * @return false
655  */
IsLanguageTag(const char * str,int32_t len)656 bool LocaleMatcher::IsLanguageTag(const char *str, int32_t len)
657 {
658     if (len < 0) {
659         len = strlen(str);
660     }
661     if (len >= 2 && len <= 3 && Utils::IsAlphaString(str, len)) {
662         return true;
663     }
664     return false;
665 }
666 
667 /**
668  * @brief script is 4 letters
669  *
670  * @param str
671  * @param len
672  * @return true
673  * @return false
674  */
IsScriptTag(const char * str,int32_t len)675 bool LocaleMatcher::IsScriptTag(const char *str, int32_t len)
676 {
677     if (len < 0) {
678         len = strlen(str);
679     }
680     if (len == 4 && Utils::IsAlphaString(str, len)) {
681         return true;
682     }
683     return false;
684 }
685 
686 /**
687  * @brief region is 2 letters or 3 digits.
688  *
689  * @param str
690  * @param len
691  * @return true
692  * @return false
693  */
IsRegionTag(const char * str,int32_t len)694 bool LocaleMatcher::IsRegionTag(const char *str, int32_t len)
695 {
696     if (len < 0) {
697         len = strlen(str);
698     }
699     if (len == 2 && Utils::IsAlphaString(str, len)) {
700         return true;
701     }
702     if (len == 3 && Utils::IsNumericString(str, len)) {
703         return true;
704     }
705     return false;
706 }
707 
IsMoreSpecificThan(const ResLocale * current,const ResLocale * other)708 int8_t LocaleMatcher::IsMoreSpecificThan(const ResLocale *current, const ResLocale *other)
709 {
710     // compare language
711     if (current == nullptr && other == nullptr) {
712         return 0;
713     }
714     if (current != nullptr && other == nullptr) {
715         return 1;
716     }
717     if (current == nullptr && other != nullptr) {
718         return -1;
719     }
720     // here language is equal,compare region
721     if (current->GetRegion() == other->GetRegion()) {
722         return 0;
723     }
724     if (current->GetRegion() == nullptr) {
725         return -1;
726     }
727     if (other->GetRegion() == nullptr) {
728         return 1;
729     }
730     return 0;
731 }
732 } // namespace Resource
733 } // namespace Global
734 } // namespace OHOS