1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef ECMASCRIPT_BASE_LOCALE_HELPER_H
17#define ECMASCRIPT_BASE_LOCALE_HELPER_H
18
19#include "ecmascript/js_handle.h"
20#include "ecmascript/js_locale.h"
21
22namespace panda::ecmascript::intl {
23constexpr uint8_t INTL_INDEX_TWO = 2;
24constexpr uint8_t INTL_INDEX_THREE = 3;
25constexpr uint8_t INTL_INDEX_FOUR = 4;
26constexpr uint8_t INTL_INDEX_FIVE = 5;
27constexpr uint8_t INTL_INDEX_EIGHT = 8;
28class LocaleHelper {
29public:
30    struct ParsedLocale {
31        std::string base;
32        std::string extension;
33    };
34    static JSHandle<EcmaString> UStringToString(JSThread *thread, const icu::UnicodeString &string);
35    static JSHandle<EcmaString> UStringToString(JSThread *thread, const icu::UnicodeString &string, int32_t begin,
36                                                int32_t end);
37
38    // 9.2.1 CanonicalizeLocaleList ( locales )
39    static JSHandle<TaggedArray> CanonicalizeLocaleList(JSThread *thread, const JSHandle<JSTaggedValue> &locales);
40    // 6.2.3 CanonicalizeUnicodeLocaleId ( locale )
41    static JSHandle<EcmaString> CanonicalizeUnicodeLocaleId(JSThread *thread, const JSHandle<EcmaString> &locale);
42    static std::string ToStdStringLanguageTag(JSThread *thread, const icu::Locale &locale);
43    static JSHandle<EcmaString> ToLanguageTag(JSThread *thread, const icu::Locale &locale);
44    static std::vector<std::string> GetAvailableLocales(JSThread *thread, const char *key, const char *path);
45    static bool IsStructurallyValidLanguageTag(const JSHandle<EcmaString> &tag);
46    // 9.2.2 BestAvailableLocale ( availableLocales, locale )
47    static std::string BestAvailableLocale(const std::vector<std::string> &availableLocales,
48                                           const std::string &locale);
49    static const std::string& StdStringDefaultLocale(JSThread *thread);
50    static JSHandle<EcmaString> DefaultLocale(JSThread *thread);
51    static LocaleHelper::ParsedLocale HandleLocale(const JSHandle<EcmaString> &localeString);
52    static LocaleHelper::ParsedLocale HandleLocale(const std::string &localeString);
53    static void HandleLocaleExtension(size_t &start, size_t &extensionEnd, const std::string result, size_t len);
54    static std::string ConvertToStdString(const JSHandle<EcmaString> &ecmaStr);
55private:
56    template<typename T>
57    static JSHandle<TaggedArray> CanonicalizeHelper(JSThread *thread, JSHandle<T> &obj, JSHandle<TaggedArray> &seen);
58    static bool DealwithLanguageTag(const std::vector<std::string> &containers, size_t &address);
59
60    static inline constexpr int AsciiAlphaToLower(uint32_t c)
61    {
62        constexpr uint32_t FLAG = 0x20;
63        return static_cast<int>(c | FLAG);
64    }
65
66    static bool IsLanguageSubtag(const std::string &value)
67    {
68        return IsAlpha(value, INTL_INDEX_TWO, INTL_INDEX_THREE) || IsAlpha(value, INTL_INDEX_FIVE, INTL_INDEX_EIGHT);
69    }
70
71    static bool IsScriptSubtag(const std::string &value)
72    {
73        return IsAlpha(value, INTL_INDEX_FOUR, INTL_INDEX_FOUR);
74    }
75
76    static bool IsRegionSubtag(const std::string &value)
77    {
78        return IsAlpha(value, INTL_INDEX_TWO, INTL_INDEX_TWO) || IsDigit(value, INTL_INDEX_THREE, INTL_INDEX_THREE);
79    }
80
81    static bool IsVariantSubtag(const std::string &value)
82    {
83        return IsThirdDigitAlphanum(value) || IsAlphanum(value, INTL_INDEX_FIVE, INTL_INDEX_EIGHT);
84    }
85
86    static bool IsThirdDigitAlphanum(const std::string &value)
87    {
88        return InRange(value[0], '0', '9') && value.length() == INTL_INDEX_FOUR &&
89            IsAlphanum(value.substr(1), INTL_INDEX_THREE, INTL_INDEX_THREE);
90    }
91
92    static bool IsExtensionSingleton(const std::string &value)
93    {
94        return IsAlphanum(value, 1, 1);
95    }
96
97    static bool IsPrivateSubTag(std::string result, size_t len)
98    {
99        if ((len > 1) && (result[1] == '-')) {
100            ASSERT(result[0] == 'x' || result[0] == 'i');
101            return true;
102        }
103        return false;
104    }
105
106    template<typename T, typename U>
107    static bool InRange(T value, U start, U end)
108    {
109        ASSERT(start <= end);
110        ASSERT(sizeof(T) >= sizeof(U));
111        return (value >= static_cast<T>(start)) && (value <= static_cast<T>(end));
112    }
113
114    static bool IsAsciiAlpha(char ch)
115    {
116        return InRange(ch, 'A', 'Z') || InRange(ch, 'a', 'z');
117    }
118
119    static bool IsAlpha(const std::string &str, size_t min, size_t max)
120    {
121        if (!InRange(str.length(), min, max)) {
122            return false;
123        }
124        for (char c : str) {
125            if (!IsAsciiAlpha(c)) {
126                return false;
127            }
128        }
129        return true;
130    }
131
132    static bool IsDigit(const std::string &str, size_t min, size_t max)
133    {
134        if (!InRange(str.length(), min, max)) {
135            return false;
136        }
137        for (char i : str) {
138            if (!InRange(i, '0', '9')) {
139                return false;
140            }
141        }
142        return true;
143    }
144
145    static bool IsAlphanum(const std::string &str, size_t min, size_t max)
146    {
147        if (!InRange(str.length(), min, max)) {
148            return false;
149        }
150        for (char i : str) {
151            if (!IsAsciiAlpha(i) && !InRange(i, '0', '9')) {
152                return false;
153            }
154        }
155        return true;
156    }
157
158    static bool ValidateOtherTags(const icu::Locale &locale, const char *packageName, const char *key, bool &res)
159    {
160        const char *localeCountry = locale.getCountry();
161        const char *localeScript = locale.getScript();
162        if (localeCountry[0] != '\0' && localeScript[0] != '\0') {
163            std::string removeCountry = locale.getLanguage();
164            removeCountry.append("-");
165            removeCountry.append(localeScript);
166            return CheckLocales(removeCountry.c_str(), key, packageName, res);
167        }
168        if (localeCountry[0] != '\0' || localeScript[0] != '\0') {
169            std::string language = locale.getLanguage();
170            return CheckLocales(language.c_str(), key, packageName, res);
171        }
172        return res;
173    }
174
175    static bool CheckLocales(const icu::Locale &locale, const char *key, const char *packageName, bool &res)
176    {
177        res = false;
178        UErrorCode status = U_ZERO_ERROR;
179        const char *formalLocale = locale.getName();
180        UResourceBundle *localeRes = ures_open(packageName, formalLocale, &status);
181        if (localeRes != nullptr && status == U_ZERO_ERROR) {
182            if (key == nullptr) {
183                res = true;
184            } else {
185                UResourceBundle *keyRes = ures_getByKey(localeRes, key, nullptr, &status);
186                if (keyRes != nullptr && status == U_ZERO_ERROR) {
187                    res = true;
188                }
189                ures_close(keyRes);
190            }
191        }
192        ures_close(localeRes);
193        if (res) {
194            return res;
195        } else {
196            ValidateOtherTags(locale, packageName, key, res);
197        }
198        return res;
199    }
200
201    static bool IsVariantSubtag(std::string substring, std::vector<std::string> containers);
202};
203}
204#endif  // ECMASCRIPT_BASE_LOCALE_HELPER_H