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 srcipt 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  * suppor 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     // 0-4 NEW/OLD language code means iw/he,tl/fil,ji/yi,jw/jv,in/id
273     return ((currentEncodedLanguage == otherEncodedLanguage) ||
274         ((currentEncodedLanguage == NEW_LANGUAGES_CODES[0])
275             && (otherEncodedLanguage == OLD_LANGUAGES_CODES[0])) ||
276         ((otherEncodedLanguage == NEW_LANGUAGES_CODES[0])
277             && (currentEncodedLanguage == OLD_LANGUAGES_CODES[0])) ||
278         ((currentEncodedLanguage == NEW_LANGUAGES_CODES[1])
279             && (otherEncodedLanguage == OLD_LANGUAGES_CODES[1])) ||
280         ((otherEncodedLanguage == NEW_LANGUAGES_CODES[1])
281             && (currentEncodedLanguage == OLD_LANGUAGES_CODES[1])) ||
282         ((currentEncodedLanguage == NEW_LANGUAGES_CODES[2])
283             && (otherEncodedLanguage == OLD_LANGUAGES_CODES[2])) ||
284         ((otherEncodedLanguage == NEW_LANGUAGES_CODES[2])
285             && (currentEncodedLanguage == OLD_LANGUAGES_CODES[2])) ||
286         ((currentEncodedLanguage == NEW_LANGUAGES_CODES[3])
287             && (otherEncodedLanguage == OLD_LANGUAGES_CODES[3])) ||
288         ((otherEncodedLanguage == NEW_LANGUAGES_CODES[3])
289             && (currentEncodedLanguage == OLD_LANGUAGES_CODES[3])) ||
290         ((currentEncodedLanguage == NEW_LANGUAGES_CODES[4])
291             && (otherEncodedLanguage == OLD_LANGUAGES_CODES[4])) ||
292         ((otherEncodedLanguage == NEW_LANGUAGES_CODES[4])
293             && (currentEncodedLanguage == OLD_LANGUAGES_CODES[4])));
294 }
295 
CompareScript(const ResLocale * current,const ResLocale * other)296 bool CompareScript(const ResLocale *current, const ResLocale *other)
297 {
298     uint32_t currentEncodedScript = 0;
299     uint32_t otherEncodedScript = 0;
300     if ((current != nullptr) && (current->GetScript() == nullptr)) {
301         currentEncodedScript = FindDefaultScriptEncode(current->GetLanguage(),
302             current->GetRegion());
303     } else {
304         currentEncodedScript = Utils::EncodeScriptByResLocale(current);
305     }
306     if ((other != nullptr) && (other->GetScript() == nullptr)) {
307         otherEncodedScript = FindDefaultScriptEncode(other->GetLanguage(),
308             other->GetRegion());
309     } else {
310         otherEncodedScript = Utils::EncodeScriptByResLocale(other);
311     }
312     if (current != nullptr && other != nullptr) {
313         // when current locale is en-Qaag is equal en-Latn
314         if (Utils::EncodeLocale(current->GetLanguage(), current->GetScript(), nullptr) ==
315             Utils::EncodeLocale("en", "Qaag", nullptr)) {
316             if (Utils::EncodeLocale(other->GetLanguage(), other->GetScript(), nullptr) ==
317                 Utils::EncodeLocale("en", "Latn", nullptr)) {
318                 return true;
319             }
320         }
321     }
322     bool compareRegion = false;
323     if ((currentEncodedScript == LocaleMatcher::NULL_SCRIPT) || (otherEncodedScript == LocaleMatcher::NULL_SCRIPT)) {
324         // if request script is null, region must be same
325         compareRegion = true;
326     }
327     if (compareRegion) {
328         uint16_t currentRegionEncode = Utils::EncodeRegionByResLocale(current);
329         uint16_t otherRegionEncode = Utils::EncodeRegionByResLocale(other);
330         return (otherRegionEncode == LocaleMatcher::NULL_REGION) || (currentRegionEncode == otherRegionEncode);
331     }
332     return currentEncodedScript == otherEncodedScript;
333 }
334 
AlphabeticallyCompare(const ResLocale * current,uint64_t currentEncodedLocale,const ResLocale * other,uint64_t otherEncodedLocale)335 int8_t AlphabeticallyCompare(const ResLocale *current,
336     uint64_t currentEncodedLocale,
337     const ResLocale *other,
338     uint64_t otherEncodedLocale)
339 {
340     if (currentEncodedLocale == otherEncodedLocale) {
341         return 0;
342     }
343     if (current == nullptr || current->GetRegion() == nullptr) {
344         return -1;
345     }
346     if (other == nullptr || other->GetRegion() == nullptr) {
347         return 1;
348     }
349     // be here region is not null
350     char currentFirstChar = (current->GetRegion())[0];
351     char otherFirstChar = (other->GetRegion())[0];
352     if (currentFirstChar >= '0' && currentFirstChar <= '9') {
353         if (otherFirstChar < '0' || otherFirstChar > '9') {
354             return -1;
355         }
356     } else {
357         if (otherFirstChar >= '0' && otherFirstChar <= '9') {
358             return 1;
359         }
360     }
361     if (currentEncodedLocale > otherEncodedLocale) {
362         return -1;
363     }
364     if (otherEncodedLocale > currentEncodedLocale) {
365         return 1;
366     }
367     return 0;
368 }
369 
CompareWhenRegionIsNull(uint16_t currentEncodedRegion,uint16_t otherEncodedRegion,const ResLocale * current,const ResLocale * other,const ResLocale * request)370 int8_t CompareWhenRegionIsNull(uint16_t currentEncodedRegion, uint16_t otherEncodedRegion,
371     const ResLocale *current,
372     const ResLocale *other,
373     const ResLocale *request)
374 {
375     if (current == nullptr || current->GetRegion() == nullptr) {
376         return 1;
377     }
378     if (other == nullptr || other->GetRegion() == nullptr) {
379         return -1;
380     }
381     int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
382     if (qaagResult != 0) {
383         return qaagResult;
384     }
385     // get request default region
386     uint16_t requestDefaultRegion =
387         FindDefaultRegionEncode((request == nullptr) ? nullptr : request->GetLanguage(),
388             (request == nullptr) ? nullptr : request->GetScript());
389     if (requestDefaultRegion == currentEncodedRegion) {
390         return 1;
391     }
392     if (requestDefaultRegion == otherEncodedRegion) {
393         return -1;
394     }
395     // current and other region is not null.alphabetically
396     uint64_t currentEncodedLocale = Utils::EncodeLocale((request == nullptr) ? nullptr : request->GetLanguage(),
397         nullptr, (current == nullptr) ? nullptr : current->GetRegion());
398     uint64_t otherEncodedLocale = Utils::EncodeLocale(
399         (request == nullptr) ? nullptr : request->GetLanguage(), nullptr, other->GetRegion());
400     return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
401 }
402 
CompareDistance(uint64_t currentEncodedLocale,uint64_t otherEncodedLocale,const uint64_t * requestEncodedTrackPath,const ResLocale * request)403 int8_t CompareDistance(uint64_t currentEncodedLocale, uint64_t otherEncodedLocale,
404     const uint64_t *requestEncodedTrackPath, const ResLocale *request)
405 {
406     uint64_t currentEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
407     FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, currentEncodedLocale, currentEncodedTrackPath);
408     uint64_t otherEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
409     FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, otherEncodedLocale, otherEncodedTrackPath);
410     const size_t currentDistance = ComputeTrackPathDistance(
411         requestEncodedTrackPath, currentEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE);
412     const size_t targetDistance = ComputeTrackPathDistance(
413         requestEncodedTrackPath, otherEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE);
414     if (currentDistance < targetDistance) {
415         return 1;
416     }
417     if (currentDistance > targetDistance) {
418         return -1;
419     }
420     return 0;
421 }
422 
CompareDefaultRegion(const ResLocale * current,const ResLocale * other,const ResLocale * request)423 int8_t CompareDefaultRegion(const ResLocale *current,
424     const ResLocale *other,
425     const ResLocale *request)
426 {
427     int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
428     if (qaagResult != 0) {
429         return qaagResult;
430     } else {
431         bool isCurrentDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage(),
432             (request == nullptr) ? nullptr : request->GetScript(),
433             (current == nullptr) ? nullptr : current->GetRegion());
434         bool isOtherDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage(),
435             (request == nullptr) ? nullptr : request->GetScript(), (other == nullptr) ? nullptr : other->GetRegion());
436         if (isCurrentDefaultRegion != isOtherDefaultRegion) {
437             if (isCurrentDefaultRegion) {
438                 return 1;
439             } else {
440                 return -1;
441             }
442         }
443     }
444     return 0;
445 }
446 
447 /**
448  * @brief compare current and target region, which is better for request.
449  * @param current current locale
450  * @param target target locale
451  * @param request request locale
452  * @return int8_t if current region is better than target region,return 1. if current region is equal target region,
453  *         return 0. If target region is better than current region, return -1.
454  */
CompareRegion(const ResLocale * current,const ResLocale * other,const ResLocale * request)455 int8_t CompareRegion(const ResLocale *current,
456                      const ResLocale *other,
457                      const ResLocale *request)
458 {
459     uint16_t currentEncodedRegion = Utils::EncodeRegionByResLocale(current);
460     uint16_t otherEncodedRegion = Utils::EncodeRegionByResLocale(other);
461     if (request == nullptr || request->GetRegion() == nullptr) {
462         return CompareWhenRegionIsNull(currentEncodedRegion, otherEncodedRegion, current, other, request);
463     }
464     uint64_t requestEncodedLocale = Utils::EncodeLocale(
465         request->GetLanguage(), nullptr, request->GetRegion());
466     uint64_t requestEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
467     FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, requestEncodedLocale, requestEncodedTrackPath);
468     uint64_t currentEncodedLocale = Utils::EncodeLocale(
469         request->GetLanguage(), nullptr, (current == nullptr) ? nullptr : current->GetRegion());
470     uint64_t otherEncodedLocale = Utils::EncodeLocale(
471         request->GetLanguage(), nullptr, (other == nullptr) ? nullptr : other->GetRegion());
472     int8_t currentMatchDistance = SearchTrackPathDistance(
473         requestEncodedTrackPath,
474         LocaleMatcher::TRACKPATH_ARRAY_SIZE,
475         currentEncodedLocale);
476     int8_t otherMatchDistance = SearchTrackPathDistance(
477         requestEncodedTrackPath,
478         LocaleMatcher::TRACKPATH_ARRAY_SIZE,
479         otherEncodedLocale);
480     if (currentMatchDistance < otherMatchDistance) {
481         return 1;
482     }
483     if (currentMatchDistance > otherMatchDistance) {
484         return -1;
485     }
486     int8_t result = CompareDistance(currentEncodedLocale, otherEncodedLocale, requestEncodedTrackPath, request);
487     if (result != 0) {
488         return result;
489     }
490     result = CompareDefaultRegion(current, other, request);
491     if (result != 0) {
492         return result;
493     }
494     uint16_t requestDefaultRegion =
495         FindDefaultRegionEncode(request->GetLanguage(), request->GetScript());
496     if (requestDefaultRegion == currentEncodedRegion) {
497         return 1;
498     }
499     if (requestDefaultRegion == otherEncodedRegion) {
500         return -1;
501     }
502     return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
503 }
504 
Match(const ResLocale * current,const ResLocale * other)505 bool LocaleMatcher::Match(const ResLocale *current, const ResLocale *other)
506 {
507     if (current == nullptr || other == nullptr) {
508         return true;
509     }
510     // language is not null.
511     bool isLanguageEqual = CompareLanguage(current, other);
512     if (!isLanguageEqual) {
513         return false;
514     }
515     return CompareScript(current, other);
516 };
517 
Normalize(ResLocale * localeInfo)518 bool LocaleMatcher::Normalize(ResLocale *localeInfo)
519 {
520     if (localeInfo == nullptr) {
521         return true;
522     }
523     if (!Utils::IsStrEmpty(localeInfo->GetScript())) {
524         return true;
525     }
526     uint32_t encodedScript = FindDefaultScriptEncode(localeInfo->GetLanguage(),
527                                                      localeInfo->GetRegion());
528     if (encodedScript == LocaleMatcher::NULL_SCRIPT) {
529         return true;
530     }
531     char *tempScript = new(std::nothrow) char[SCRIPT_ARRAY_LEN];
532     if (tempScript == nullptr) {
533         return false;
534     }
535     tempScript[SCRIPT_LEN] = '\0';
536     Utils::DecodeScript(encodedScript, tempScript);
537     localeInfo->script_ = tempScript;
538     return true;
539 }
540 
CompareLanguageIgnoreOldNewCode(const ResLocale * current,const ResLocale * other,const ResLocale * request)541 int8_t CompareLanguageIgnoreOldNewCode(const ResLocale *current, const ResLocale *other, const ResLocale *request)
542 {
543     uint16_t currentLanguageEncode = Utils::EncodeLanguageByResLocale(current);
544     uint16_t otherLanguageEncode = Utils::EncodeLanguageByResLocale(other);
545     uint16_t requestLanguageEncode = Utils::EncodeLanguageByResLocale(request);
546     if ((currentLanguageEncode == requestLanguageEncode) && (otherLanguageEncode != requestLanguageEncode)) {
547         return 1;
548     }
549     if ((otherLanguageEncode == requestLanguageEncode) && (currentLanguageEncode != requestLanguageEncode)) {
550         return -1;
551     }
552     return 0;
553 }
554 
IsSimilarToUsEnglish(const ResLocale * localeInfo)555 bool IsSimilarToUsEnglish(const ResLocale *localeInfo)
556 {
557     uint64_t localeEncode = Utils::EncodeLocale("en", nullptr,
558         (localeInfo == nullptr) ? nullptr : localeInfo->GetRegion());
559     uint64_t loclaeEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
560     FindTrackPath(nullptr, LocaleMatcher::TRACKPATH_ARRAY_SIZE, localeEncode, loclaeEncodedTrackPath);
561     uint8_t len = LocaleMatcher::TRACKPATH_ARRAY_SIZE;
562     for (uint8_t i = 0; i < len; ++i) {
563         if (loclaeEncodedTrackPath[i] == Utils::EncodeLocale("en", nullptr, nullptr)) {
564             return true;
565         }
566         if (loclaeEncodedTrackPath[i] == Utils::EncodeLocale("en", nullptr, "001")) {
567             return false;
568         }
569     }
570     return   false;
571 }
CompareRegionWhenLangIsNotEqual(const ResLocale * current,const ResLocale * other,const ResLocale * request)572 bool CompareRegionWhenLangIsNotEqual(const ResLocale *current,
573     const ResLocale *other,
574     const ResLocale *request)
575 {
576     int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
577     if (qaagResult != 0) {
578         return qaagResult;
579     }
580     if (request != nullptr && (Utils::EncodeLanguage(request->GetLanguage())) == Utils::EncodeLanguage("en")) {
581         // when request is en-us,empty region is better
582         if ((Utils::EncodeRegion(request->GetRegion())) == Utils::EncodeRegion("US")) {
583             if (current != nullptr) {
584                 return (current->GetRegion() == nullptr) ||
585                             ((Utils::EncodeRegion(current->GetRegion())) == Utils::EncodeRegion("US"));
586             } else {
587                 return !(other->GetRegion() == nullptr ||
588                             ((Utils::EncodeRegion(other->GetRegion())) == Utils::EncodeRegion("US")));
589             }
590         } else if (IsSimilarToUsEnglish(request)) {
591             if (current != nullptr) {
592                 return IsSimilarToUsEnglish(current);
593             } else {
594                 return !IsSimilarToUsEnglish(other);
595             }
596         }
597     }
598     return current != nullptr;
599 }
600 
IsMoreSuitable(const ResLocale * current,const ResLocale * other,const ResLocale * request)601 int8_t LocaleMatcher::IsMoreSuitable(const ResLocale *current,
602     const ResLocale *other,
603     const ResLocale *request)
604 {
605     if (request == nullptr) {
606         return 0;
607     }
608     if (current == nullptr && other == nullptr) {
609         return 0;
610     }
611     bool isLangEqual = CompareLanguage(current, other);
612     if (!isLangEqual) {
613         // current or other language is null, not null language is better
614         bool result = CompareRegionWhenLangIsNotEqual(current, other, request);
615         return result ? 1 : -1;
616     }
617     uint16_t currentEncodedRegion =
618         Utils::EncodeRegionByResLocale(current);
619     uint16_t otherEncodedRegion =
620         Utils::EncodeRegionByResLocale(other);
621     if (currentEncodedRegion == otherEncodedRegion) {
622         // same language,same script,same region
623         return CompareLanguageIgnoreOldNewCode(current, other, request);
624     }
625     // equal request region is better
626     uint16_t requestEncodedRegion = Utils::EncodeRegionByResLocale(request);
627     if (currentEncodedRegion == requestEncodedRegion) {
628         return 1;
629     }
630     if (otherEncodedRegion == requestEncodedRegion) {
631         return -1;
632     }
633     int8_t isRegionEqual = CompareRegion(current, other, request);
634     if (isRegionEqual == 0) {
635         return CompareLanguageIgnoreOldNewCode(current, other, request);
636     }
637     return isRegionEqual;
638 };
639 
640 /**
641  * @brief language tag is 2 or 3 letters
642  *
643  * @param str
644  * @param len
645  * @return true
646  * @return false
647  */
IsLanguageTag(const char * str,int32_t len)648 bool LocaleMatcher::IsLanguageTag(const char *str, int32_t len)
649 {
650     if (len < 0) {
651         len = strlen(str);
652     }
653     if (len >= 2 && len <= 3 && Utils::IsAlphaString(str, len)) {
654         return true;
655     }
656     return false;
657 }
658 
659 /**
660  * @brief script is 4 letters
661  *
662  * @param str
663  * @param len
664  * @return true
665  * @return false
666  */
IsScriptTag(const char * str,int32_t len)667 bool LocaleMatcher::IsScriptTag(const char *str, int32_t len)
668 {
669     if (len < 0) {
670         len = strlen(str);
671     }
672     if (len == 4 && Utils::IsAlphaString(str, len)) {
673         return true;
674     }
675     return false;
676 }
677 
678 /**
679  * @brief region is 2 letters or 3 digits.
680  *
681  * @param str
682  * @param len
683  * @return true
684  * @return false
685  */
IsRegionTag(const char * str,int32_t len)686 bool LocaleMatcher::IsRegionTag(const char *str, int32_t len)
687 {
688     if (len < 0) {
689         len = strlen(str);
690     }
691     // region is 2 letters if is alpha string
692     if (len == 2 && Utils::IsAlphaString(str, len)) {
693         return true;
694     }
695     // region is 3 letters if is numeric string
696     if (len == 3 && Utils::IsNumericString(str, len)) {
697         return true;
698     }
699     return false;
700 }
701 
IsMoreSpecificThan(const ResLocale * current,const ResLocale * other)702 int8_t LocaleMatcher::IsMoreSpecificThan(const ResLocale *current, const ResLocale *other)
703 {
704     // compare language
705     if (current == nullptr && other == nullptr) {
706         return 0;
707     }
708     if (current != nullptr && other == nullptr) {
709         return 1;
710     }
711     if (current == nullptr && other != nullptr) {
712         return -1;
713     }
714     // here language is equal,compare region
715     if (current->GetRegion() == other->GetRegion()) {
716         return 0;
717     }
718     if (current->GetRegion() == nullptr) {
719         return -1;
720     }
721     if (other->GetRegion() == nullptr) {
722         return 1;
723     }
724     return 0;
725 }
726 } // namespace Resource
727 } // namespace Global
728 } // namespace OHOS