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