14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2021 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#include "ecmascript/js_plural_rules.h"
174514f5e3Sopenharmony_ci
184514f5e3Sopenharmony_ci#include "ecmascript/object_factory-inl.h"
194514f5e3Sopenharmony_ci#include "ecmascript/js_number_format.h"
204514f5e3Sopenharmony_ci#include "ecmascript/checkpoint/thread_state_transition.h"
214514f5e3Sopenharmony_ci
224514f5e3Sopenharmony_cinamespace panda::ecmascript {
234514f5e3Sopenharmony_ciconstexpr int32_t STRING_SEPARATOR_LENGTH = 4;
244514f5e3Sopenharmony_ci
254514f5e3Sopenharmony_ciicu::number::LocalizedNumberFormatter *JSPluralRules::GetIcuNumberFormatter() const
264514f5e3Sopenharmony_ci{
274514f5e3Sopenharmony_ci    ASSERT(GetIcuNF().IsJSNativePointer());
284514f5e3Sopenharmony_ci    auto result = JSNativePointer::Cast(GetIcuNF().GetTaggedObject())->GetExternalPointer();
294514f5e3Sopenharmony_ci    return reinterpret_cast<icu::number::LocalizedNumberFormatter *>(result);
304514f5e3Sopenharmony_ci}
314514f5e3Sopenharmony_ci
324514f5e3Sopenharmony_civoid JSPluralRules::FreeIcuNumberFormatter([[maybe_unused]] void *env, void *pointer, void* hint)
334514f5e3Sopenharmony_ci{
344514f5e3Sopenharmony_ci    if (pointer == nullptr) {
354514f5e3Sopenharmony_ci        return;
364514f5e3Sopenharmony_ci    }
374514f5e3Sopenharmony_ci    auto icuNumberFormatter = reinterpret_cast<icu::number::LocalizedNumberFormatter *>(pointer);
384514f5e3Sopenharmony_ci    icuNumberFormatter->~LocalizedNumberFormatter();
394514f5e3Sopenharmony_ci    if (hint != nullptr) {
404514f5e3Sopenharmony_ci        reinterpret_cast<EcmaVM *>(hint)->GetNativeAreaAllocator()->FreeBuffer(pointer);
414514f5e3Sopenharmony_ci    }
424514f5e3Sopenharmony_ci}
434514f5e3Sopenharmony_ci
444514f5e3Sopenharmony_civoid JSPluralRules::SetIcuNumberFormatter(JSThread *thread, const JSHandle<JSPluralRules> &pluralRules,
454514f5e3Sopenharmony_ci    const icu::number::LocalizedNumberFormatter &icuNF, const NativePointerCallback &callback)
464514f5e3Sopenharmony_ci{
474514f5e3Sopenharmony_ci    EcmaVM *ecmaVm = thread->GetEcmaVM();
484514f5e3Sopenharmony_ci    ObjectFactory *factory = ecmaVm->GetFactory();
494514f5e3Sopenharmony_ci
504514f5e3Sopenharmony_ci    icu::number::LocalizedNumberFormatter *icuPointer =
514514f5e3Sopenharmony_ci        ecmaVm->GetNativeAreaAllocator()->New<icu::number::LocalizedNumberFormatter>(icuNF);
524514f5e3Sopenharmony_ci    ASSERT(icuPointer != nullptr);
534514f5e3Sopenharmony_ci    JSTaggedValue data = pluralRules->GetIcuNF();
544514f5e3Sopenharmony_ci    if (data.IsHeapObject() && data.IsJSNativePointer()) {
554514f5e3Sopenharmony_ci        JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject());
564514f5e3Sopenharmony_ci        native->ResetExternalPointer(thread, icuPointer);
574514f5e3Sopenharmony_ci        return;
584514f5e3Sopenharmony_ci    }
594514f5e3Sopenharmony_ci    JSHandle<JSNativePointer> pointer = factory->NewJSNativePointer(icuPointer, callback, ecmaVm);
604514f5e3Sopenharmony_ci    pluralRules->SetIcuNF(thread, pointer.GetTaggedValue());
614514f5e3Sopenharmony_ci}
624514f5e3Sopenharmony_ci
634514f5e3Sopenharmony_ciicu::PluralRules *JSPluralRules::GetIcuPluralRules() const
644514f5e3Sopenharmony_ci{
654514f5e3Sopenharmony_ci    ASSERT(GetIcuPR().IsJSNativePointer());
664514f5e3Sopenharmony_ci    auto result = JSNativePointer::Cast(GetIcuPR().GetTaggedObject())->GetExternalPointer();
674514f5e3Sopenharmony_ci    return reinterpret_cast<icu::PluralRules *>(result);
684514f5e3Sopenharmony_ci}
694514f5e3Sopenharmony_ci
704514f5e3Sopenharmony_civoid JSPluralRules::FreeIcuPluralRules([[maybe_unused]] void *env, void *pointer, void* hint)
714514f5e3Sopenharmony_ci{
724514f5e3Sopenharmony_ci    if (pointer == nullptr) {
734514f5e3Sopenharmony_ci        return;
744514f5e3Sopenharmony_ci    }
754514f5e3Sopenharmony_ci    auto icuPluralRules = reinterpret_cast<icu::PluralRules *>(pointer);
764514f5e3Sopenharmony_ci    icuPluralRules->~PluralRules();
774514f5e3Sopenharmony_ci    if (hint != nullptr) {
784514f5e3Sopenharmony_ci        reinterpret_cast<EcmaVM *>(hint)->GetNativeAreaAllocator()->FreeBuffer(pointer);
794514f5e3Sopenharmony_ci    }
804514f5e3Sopenharmony_ci}
814514f5e3Sopenharmony_ci
824514f5e3Sopenharmony_civoid JSPluralRules::SetIcuPluralRules(JSThread *thread, const JSHandle<JSPluralRules> &pluralRules,
834514f5e3Sopenharmony_ci    const icu::PluralRules &icuPR, const NativePointerCallback &callback)
844514f5e3Sopenharmony_ci{
854514f5e3Sopenharmony_ci    [[maybe_unused]] EcmaHandleScope scope(thread);
864514f5e3Sopenharmony_ci    EcmaVM *ecmaVm = thread->GetEcmaVM();
874514f5e3Sopenharmony_ci    ObjectFactory *factory = ecmaVm->GetFactory();
884514f5e3Sopenharmony_ci
894514f5e3Sopenharmony_ci    icu::PluralRules *icuPointer = ecmaVm->GetNativeAreaAllocator()->New<icu::PluralRules>(icuPR);
904514f5e3Sopenharmony_ci    ASSERT(icuPointer != nullptr);
914514f5e3Sopenharmony_ci    JSTaggedValue data = pluralRules->GetIcuPR();
924514f5e3Sopenharmony_ci    if (data.IsHeapObject() && data.IsJSNativePointer()) {
934514f5e3Sopenharmony_ci        JSNativePointer *native = JSNativePointer::Cast(data.GetTaggedObject());
944514f5e3Sopenharmony_ci        native->ResetExternalPointer(thread, icuPointer);
954514f5e3Sopenharmony_ci        return;
964514f5e3Sopenharmony_ci    }
974514f5e3Sopenharmony_ci    JSHandle<JSNativePointer> pointer = factory->NewJSNativePointer(icuPointer, callback, ecmaVm);
984514f5e3Sopenharmony_ci    pluralRules->SetIcuPR(thread, pointer.GetTaggedValue());
994514f5e3Sopenharmony_ci}
1004514f5e3Sopenharmony_ci
1014514f5e3Sopenharmony_ciJSHandle<TaggedArray> JSPluralRules::BuildLocaleSet(JSThread *thread, const std::set<std::string> &icuAvailableLocales)
1024514f5e3Sopenharmony_ci{
1034514f5e3Sopenharmony_ci    EcmaVM *ecmaVm = thread->GetEcmaVM();
1044514f5e3Sopenharmony_ci    ObjectFactory *factory = ecmaVm->GetFactory();
1054514f5e3Sopenharmony_ci    JSHandle<TaggedArray> locales = factory->NewTaggedArray(icuAvailableLocales.size());
1064514f5e3Sopenharmony_ci    int32_t index = 0;
1074514f5e3Sopenharmony_ci
1084514f5e3Sopenharmony_ci    for (const std::string &locale : icuAvailableLocales) {
1094514f5e3Sopenharmony_ci        JSHandle<EcmaString> localeStr = factory->NewFromStdString(locale);
1104514f5e3Sopenharmony_ci        locales->Set(thread, index++, localeStr);
1114514f5e3Sopenharmony_ci    }
1124514f5e3Sopenharmony_ci    return locales;
1134514f5e3Sopenharmony_ci}
1144514f5e3Sopenharmony_ci
1154514f5e3Sopenharmony_cibool GetNextLocale(icu::StringEnumeration *locales, std::string &localeStr, int32_t *len)
1164514f5e3Sopenharmony_ci{
1174514f5e3Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
1184514f5e3Sopenharmony_ci    const char *locale = nullptr;
1194514f5e3Sopenharmony_ci    locale = locales->next(len, status);
1204514f5e3Sopenharmony_ci    if (!U_SUCCESS(status) || locale == nullptr) {
1214514f5e3Sopenharmony_ci        localeStr = "";
1224514f5e3Sopenharmony_ci        return false;
1234514f5e3Sopenharmony_ci    }
1244514f5e3Sopenharmony_ci    localeStr = std::string(locale);
1254514f5e3Sopenharmony_ci    return true;
1264514f5e3Sopenharmony_ci}
1274514f5e3Sopenharmony_ci
1284514f5e3Sopenharmony_ciJSHandle<TaggedArray> JSPluralRules::GetAvailableLocales(JSThread *thread)
1294514f5e3Sopenharmony_ci{
1304514f5e3Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
1314514f5e3Sopenharmony_ci    std::unique_ptr<icu::StringEnumeration> locales(icu::PluralRules::getAvailableLocales(status));
1324514f5e3Sopenharmony_ci    ASSERT(U_SUCCESS(status));
1334514f5e3Sopenharmony_ci    std::set<std::string> set;
1344514f5e3Sopenharmony_ci    std::string localeStr;
1354514f5e3Sopenharmony_ci    int32_t len = 0;
1364514f5e3Sopenharmony_ci    {
1374514f5e3Sopenharmony_ci        ThreadNativeScope nativeScope(thread);
1384514f5e3Sopenharmony_ci        while (GetNextLocale(locales.get(), localeStr, &len)) {
1394514f5e3Sopenharmony_ci            if (len >= STRING_SEPARATOR_LENGTH) {
1404514f5e3Sopenharmony_ci                std::replace(localeStr.begin(), localeStr.end(), '_', '-');
1414514f5e3Sopenharmony_ci            }
1424514f5e3Sopenharmony_ci            set.insert(localeStr);
1434514f5e3Sopenharmony_ci        }
1444514f5e3Sopenharmony_ci    }
1454514f5e3Sopenharmony_ci    return BuildLocaleSet(thread, set);
1464514f5e3Sopenharmony_ci}
1474514f5e3Sopenharmony_ci
1484514f5e3Sopenharmony_ci// InitializePluralRules ( pluralRules, locales, options )
1494514f5e3Sopenharmony_ciJSHandle<JSPluralRules> JSPluralRules::InitializePluralRules(JSThread *thread,
1504514f5e3Sopenharmony_ci                                                             const JSHandle<JSPluralRules> &pluralRules,
1514514f5e3Sopenharmony_ci                                                             const JSHandle<JSTaggedValue> &locales,
1524514f5e3Sopenharmony_ci                                                             const JSHandle<JSTaggedValue> &options)
1534514f5e3Sopenharmony_ci{
1544514f5e3Sopenharmony_ci    EcmaVM *ecmaVm = thread->GetEcmaVM();
1554514f5e3Sopenharmony_ci    ObjectFactory *factory = ecmaVm->GetFactory();
1564514f5e3Sopenharmony_ci    auto globalConst = thread->GlobalConstants();
1574514f5e3Sopenharmony_ci
1584514f5e3Sopenharmony_ci    // 1. Let requestedLocales be ? CanonicalizeLocaleList(locales).
1594514f5e3Sopenharmony_ci    JSHandle<TaggedArray> requestedLocales = intl::LocaleHelper::CanonicalizeLocaleList(thread, locales);
1604514f5e3Sopenharmony_ci    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread);
1614514f5e3Sopenharmony_ci
1624514f5e3Sopenharmony_ci    // 2&3. If options is undefined, then Let options be ObjectCreate(null). else Let options be ? ToObject(options).
1634514f5e3Sopenharmony_ci    JSHandle<JSObject> prOptions;
1644514f5e3Sopenharmony_ci    if (!options->IsUndefined()) {
1654514f5e3Sopenharmony_ci        prOptions = JSTaggedValue::ToObject(thread, options);
1664514f5e3Sopenharmony_ci        RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread);
1674514f5e3Sopenharmony_ci    } else {
1684514f5e3Sopenharmony_ci        prOptions = factory->CreateNullJSObject();
1694514f5e3Sopenharmony_ci    }
1704514f5e3Sopenharmony_ci
1714514f5e3Sopenharmony_ci    // 5. Let matcher be ? GetOption(options, "localeMatcher", "string", « "lookup", "best fit" », "best fit").
1724514f5e3Sopenharmony_ci    LocaleMatcherOption matcher =
1734514f5e3Sopenharmony_ci        JSLocale::GetOptionOfString(thread, prOptions, globalConst->GetHandledLocaleMatcherString(),
1744514f5e3Sopenharmony_ci                                    {LocaleMatcherOption::LOOKUP, LocaleMatcherOption::BEST_FIT},
1754514f5e3Sopenharmony_ci                                    {"lookup", "best fit"}, LocaleMatcherOption::BEST_FIT);
1764514f5e3Sopenharmony_ci    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread);
1774514f5e3Sopenharmony_ci
1784514f5e3Sopenharmony_ci    // 7. Let t be ? GetOption(options, "type", "string", « "cardinal", "ordinal" », "cardinal").
1794514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> property = JSHandle<JSTaggedValue>::Cast(globalConst->GetHandledTypeString());
1804514f5e3Sopenharmony_ci    TypeOption type =
1814514f5e3Sopenharmony_ci        JSLocale::GetOptionOfString(thread, prOptions, property, { TypeOption::CARDINAL, TypeOption::ORDINAL },
1824514f5e3Sopenharmony_ci                                    { "cardinal", "ordinal" }, TypeOption::CARDINAL);
1834514f5e3Sopenharmony_ci    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread);
1844514f5e3Sopenharmony_ci
1854514f5e3Sopenharmony_ci    // set pluralRules.[[type]] to type
1864514f5e3Sopenharmony_ci    pluralRules->SetType(type);
1874514f5e3Sopenharmony_ci
1884514f5e3Sopenharmony_ci    // Let r be ResolveLocale(%PluralRules%.[[AvailableLocales]], requestedLocales, opt,
1894514f5e3Sopenharmony_ci    // %PluralRules%.[[RelevantExtensionKeys]], localeData).
1904514f5e3Sopenharmony_ci    JSHandle<TaggedArray> availableLocales;
1914514f5e3Sopenharmony_ci    if (requestedLocales->GetLength() == 0) {
1924514f5e3Sopenharmony_ci        availableLocales = factory->EmptyArray();
1934514f5e3Sopenharmony_ci    } else {
1944514f5e3Sopenharmony_ci        availableLocales = GetAvailableLocales(thread);
1954514f5e3Sopenharmony_ci    }
1964514f5e3Sopenharmony_ci    std::set<std::string> relevantExtensionKeys{""};
1974514f5e3Sopenharmony_ci    ResolvedLocale r =
1984514f5e3Sopenharmony_ci        JSLocale::ResolveLocale(thread, availableLocales, requestedLocales, matcher, relevantExtensionKeys);
1994514f5e3Sopenharmony_ci    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread);
2004514f5e3Sopenharmony_ci    icu::Locale icuLocale = r.localeData;
2014514f5e3Sopenharmony_ci
2024514f5e3Sopenharmony_ci    // Get ICU numberFormatter with given locale
2034514f5e3Sopenharmony_ci    icu::number::LocalizedNumberFormatter icuNumberFormatter =
2044514f5e3Sopenharmony_ci        icu::number::NumberFormatter::withLocale(icuLocale).roundingMode(UNUM_ROUND_HALFUP);
2054514f5e3Sopenharmony_ci
2064514f5e3Sopenharmony_ci    bool success = true;
2074514f5e3Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
2084514f5e3Sopenharmony_ci    UPluralType icuType = UPLURAL_TYPE_CARDINAL;
2094514f5e3Sopenharmony_ci    // Trans typeOption to ICU typeOption
2104514f5e3Sopenharmony_ci    switch (type) {
2114514f5e3Sopenharmony_ci        case TypeOption::ORDINAL:
2124514f5e3Sopenharmony_ci            icuType = UPLURAL_TYPE_ORDINAL;
2134514f5e3Sopenharmony_ci            break;
2144514f5e3Sopenharmony_ci        case TypeOption::CARDINAL:
2154514f5e3Sopenharmony_ci            icuType = UPLURAL_TYPE_CARDINAL;
2164514f5e3Sopenharmony_ci            break;
2174514f5e3Sopenharmony_ci        default:
2184514f5e3Sopenharmony_ci            LOG_ECMA(FATAL) << "this branch is unreachable";
2194514f5e3Sopenharmony_ci            UNREACHABLE();
2204514f5e3Sopenharmony_ci    }
2214514f5e3Sopenharmony_ci    std::unique_ptr<icu::PluralRules> icuPluralRules(icu::PluralRules::forLocale(icuLocale, icuType, status));
2224514f5e3Sopenharmony_ci    if (U_FAILURE(status)) {  // NOLINT(readability-implicit-bool-conversion)
2234514f5e3Sopenharmony_ci        success = false;
2244514f5e3Sopenharmony_ci    }
2254514f5e3Sopenharmony_ci
2264514f5e3Sopenharmony_ci    // Trans typeOption to ICU typeOption
2274514f5e3Sopenharmony_ci    if (!success || icuPluralRules == nullptr) {
2284514f5e3Sopenharmony_ci        icu::Locale noExtensionLocale(icuLocale.getBaseName());
2294514f5e3Sopenharmony_ci        status = U_ZERO_ERROR;
2304514f5e3Sopenharmony_ci        switch (type) {
2314514f5e3Sopenharmony_ci            case TypeOption::ORDINAL:
2324514f5e3Sopenharmony_ci                icuType = UPLURAL_TYPE_ORDINAL;
2334514f5e3Sopenharmony_ci                break;
2344514f5e3Sopenharmony_ci            case TypeOption::CARDINAL:
2354514f5e3Sopenharmony_ci                icuType = UPLURAL_TYPE_CARDINAL;
2364514f5e3Sopenharmony_ci                break;
2374514f5e3Sopenharmony_ci            default:
2384514f5e3Sopenharmony_ci                LOG_ECMA(FATAL) << "this branch is unreachable";
2394514f5e3Sopenharmony_ci                UNREACHABLE();
2404514f5e3Sopenharmony_ci        }
2414514f5e3Sopenharmony_ci        icuPluralRules.reset(icu::PluralRules::forLocale(icuLocale, icuType, status));
2424514f5e3Sopenharmony_ci    }
2434514f5e3Sopenharmony_ci    if (U_FAILURE(status) || icuPluralRules == nullptr) {  // NOLINT(readability-implicit-bool-conversion)
2444514f5e3Sopenharmony_ci        THROW_RANGE_ERROR_AND_RETURN(thread, "cannot create icuPluralRules", pluralRules);
2454514f5e3Sopenharmony_ci    }
2464514f5e3Sopenharmony_ci
2474514f5e3Sopenharmony_ci    // 9. Perform ? SetNumberFormatDigitOptions(pluralRules, options, 0, 3, "standard").
2484514f5e3Sopenharmony_ci    JSLocale::SetNumberFormatDigitOptions(thread, pluralRules, JSHandle<JSTaggedValue>::Cast(prOptions), MNFD_DEFAULT,
2494514f5e3Sopenharmony_ci                                          MXFD_DEFAULT, NotationOption::STANDARD);
2504514f5e3Sopenharmony_ci    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread);
2514514f5e3Sopenharmony_ci    icuNumberFormatter = JSNumberFormat::SetICUFormatterDigitOptions(icuNumberFormatter, pluralRules);
2524514f5e3Sopenharmony_ci
2534514f5e3Sopenharmony_ci    // Set pluralRules.[[IcuPluralRules]] to icuPluralRules
2544514f5e3Sopenharmony_ci    SetIcuPluralRules(thread, pluralRules, *icuPluralRules, JSPluralRules::FreeIcuPluralRules);
2554514f5e3Sopenharmony_ci
2564514f5e3Sopenharmony_ci    // Set pluralRules.[[IcuNumberFormat]] to icuNumberFormatter
2574514f5e3Sopenharmony_ci    SetIcuNumberFormatter(thread, pluralRules, icuNumberFormatter, JSPluralRules::FreeIcuNumberFormatter);
2584514f5e3Sopenharmony_ci
2594514f5e3Sopenharmony_ci    // 12. Set pluralRules.[[Locale]] to the value of r.[[locale]].
2604514f5e3Sopenharmony_ci    JSHandle<EcmaString> localeStr = intl::LocaleHelper::ToLanguageTag(thread, icuLocale);
2614514f5e3Sopenharmony_ci    RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSPluralRules, thread);
2624514f5e3Sopenharmony_ci    pluralRules->SetLocale(thread, localeStr.GetTaggedValue());
2634514f5e3Sopenharmony_ci
2644514f5e3Sopenharmony_ci    // 13. Return pluralRules.
2654514f5e3Sopenharmony_ci    return pluralRules;
2664514f5e3Sopenharmony_ci}
2674514f5e3Sopenharmony_ci
2684514f5e3Sopenharmony_ciJSHandle<EcmaString> FormatNumericToString(JSThread *thread, const icu::number::LocalizedNumberFormatter *icuFormatter,
2694514f5e3Sopenharmony_ci                                           const icu::PluralRules *icuPluralRules, double n)
2704514f5e3Sopenharmony_ci{
2714514f5e3Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
2724514f5e3Sopenharmony_ci    icu::number::FormattedNumber formatted = icuFormatter->formatDouble(n, status);
2734514f5e3Sopenharmony_ci    if (U_FAILURE(status)) {  // NOLINT(readability-implicit-bool-conversion)
2744514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
2754514f5e3Sopenharmony_ci        THROW_RANGE_ERROR_AND_RETURN(thread, "invalid resolve number", JSHandle<EcmaString>::Cast(exception));
2764514f5e3Sopenharmony_ci    }
2774514f5e3Sopenharmony_ci
2784514f5e3Sopenharmony_ci    icu::UnicodeString uString = icuPluralRules->select(formatted, status);
2794514f5e3Sopenharmony_ci    if (U_FAILURE(status)) {  // NOLINT(readability-implicit-bool-conversion)
2804514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> exception(thread, JSTaggedValue::Exception());
2814514f5e3Sopenharmony_ci        THROW_RANGE_ERROR_AND_RETURN(thread, "invalid resolve number", JSHandle<EcmaString>::Cast(exception));
2824514f5e3Sopenharmony_ci    }
2834514f5e3Sopenharmony_ci
2844514f5e3Sopenharmony_ci    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
2854514f5e3Sopenharmony_ci    JSHandle<EcmaString> result =
2864514f5e3Sopenharmony_ci        factory->NewFromUtf16(reinterpret_cast<const uint16_t *>(uString.getBuffer()), uString.length());
2874514f5e3Sopenharmony_ci    return result;
2884514f5e3Sopenharmony_ci}
2894514f5e3Sopenharmony_ciJSHandle<EcmaString> JSPluralRules::ResolvePlural(JSThread *thread, const JSHandle<JSPluralRules> &pluralRules,
2904514f5e3Sopenharmony_ci                                                  double n)
2914514f5e3Sopenharmony_ci{
2924514f5e3Sopenharmony_ci    icu::PluralRules *icuPluralRules = pluralRules->GetIcuPluralRules();
2934514f5e3Sopenharmony_ci    icu::number::LocalizedNumberFormatter *icuFormatter = pluralRules->GetIcuNumberFormatter();
2944514f5e3Sopenharmony_ci    if (icuPluralRules == nullptr || icuFormatter == nullptr) {
2954514f5e3Sopenharmony_ci        return JSHandle<EcmaString>(thread, JSTaggedValue::Undefined());
2964514f5e3Sopenharmony_ci    }
2974514f5e3Sopenharmony_ci
2984514f5e3Sopenharmony_ci    JSHandle<EcmaString> result = FormatNumericToString(thread, icuFormatter, icuPluralRules, n);
2994514f5e3Sopenharmony_ci    RETURN_HANDLE_IF_ABRUPT_COMPLETION(EcmaString, thread);
3004514f5e3Sopenharmony_ci    return result;
3014514f5e3Sopenharmony_ci}
3024514f5e3Sopenharmony_ci
3034514f5e3Sopenharmony_civoid JSPluralRules::ResolvedOptions(JSThread *thread, const JSHandle<JSPluralRules> &pluralRules,
3044514f5e3Sopenharmony_ci                                    const JSHandle<JSObject> &options)
3054514f5e3Sopenharmony_ci{
3064514f5e3Sopenharmony_ci    EcmaVM *ecmaVm = thread->GetEcmaVM();
3074514f5e3Sopenharmony_ci    ObjectFactory *factory = ecmaVm->GetFactory();
3084514f5e3Sopenharmony_ci    auto globalConst = thread->GlobalConstants();
3094514f5e3Sopenharmony_ci
3104514f5e3Sopenharmony_ci    // [[Locale]]
3114514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> property = JSHandle<JSTaggedValue>::Cast(globalConst->GetHandledLocaleString());
3124514f5e3Sopenharmony_ci    JSHandle<EcmaString> locale(thread, pluralRules->GetLocale());
3134514f5e3Sopenharmony_ci    PropertyDescriptor localeDesc(thread, JSHandle<JSTaggedValue>::Cast(locale), true, true, true);
3144514f5e3Sopenharmony_ci    JSObject::DefineOwnProperty(thread, options, property, localeDesc);
3154514f5e3Sopenharmony_ci
3164514f5e3Sopenharmony_ci    // [[type]]
3174514f5e3Sopenharmony_ci    property = JSHandle<JSTaggedValue>::Cast(globalConst->GetHandledTypeString());
3184514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> typeValue;
3194514f5e3Sopenharmony_ci    if (pluralRules->GetType() == TypeOption::CARDINAL) {
3204514f5e3Sopenharmony_ci        typeValue = globalConst->GetHandledCardinalString();
3214514f5e3Sopenharmony_ci    } else {
3224514f5e3Sopenharmony_ci        typeValue = globalConst->GetHandledOrdinalString();
3234514f5e3Sopenharmony_ci    }
3244514f5e3Sopenharmony_ci    PropertyDescriptor typeDesc(thread, typeValue, true, true, true);
3254514f5e3Sopenharmony_ci    JSObject::DefineOwnProperty(thread, options, property, typeDesc);
3264514f5e3Sopenharmony_ci
3274514f5e3Sopenharmony_ci    // [[MinimumIntegerDigits]]
3284514f5e3Sopenharmony_ci    property = JSHandle<JSTaggedValue>::Cast(globalConst->GetHandledMinimumIntegerDigitsString());
3294514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> minimumIntegerDigits(thread, pluralRules->GetMinimumIntegerDigits());
3304514f5e3Sopenharmony_ci    JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumIntegerDigits);
3314514f5e3Sopenharmony_ci    RETURN_IF_ABRUPT_COMPLETION(thread);
3324514f5e3Sopenharmony_ci
3334514f5e3Sopenharmony_ci    RoundingType roundingType = pluralRules->GetRoundingType();
3344514f5e3Sopenharmony_ci    if (roundingType == RoundingType::SIGNIFICANTDIGITS) {
3354514f5e3Sopenharmony_ci        // [[MinimumSignificantDigits]]
3364514f5e3Sopenharmony_ci        property = globalConst->GetHandledMinimumSignificantDigitsString();
3374514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> minimumSignificantDigits(thread, pluralRules->GetMinimumSignificantDigits());
3384514f5e3Sopenharmony_ci        JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumSignificantDigits);
3394514f5e3Sopenharmony_ci        RETURN_IF_ABRUPT_COMPLETION(thread);
3404514f5e3Sopenharmony_ci        // [[MaximumSignificantDigits]]
3414514f5e3Sopenharmony_ci        property = globalConst->GetHandledMaximumSignificantDigitsString();
3424514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> maximumSignificantDigits(thread, pluralRules->GetMaximumSignificantDigits());
3434514f5e3Sopenharmony_ci        JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumSignificantDigits);
3444514f5e3Sopenharmony_ci        RETURN_IF_ABRUPT_COMPLETION(thread);
3454514f5e3Sopenharmony_ci    } else {
3464514f5e3Sopenharmony_ci        // [[MinimumFractionDigits]]
3474514f5e3Sopenharmony_ci        property = globalConst->GetHandledMinimumFractionDigitsString();
3484514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> minimumFractionDigits(thread, pluralRules->GetMinimumFractionDigits());
3494514f5e3Sopenharmony_ci        JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumFractionDigits);
3504514f5e3Sopenharmony_ci        RETURN_IF_ABRUPT_COMPLETION(thread);
3514514f5e3Sopenharmony_ci        // [[MaximumFractionDigits]]
3524514f5e3Sopenharmony_ci        property = globalConst->GetHandledMaximumFractionDigitsString();
3534514f5e3Sopenharmony_ci        JSHandle<JSTaggedValue> maximumFractionDigits(thread, pluralRules->GetMaximumFractionDigits());
3544514f5e3Sopenharmony_ci        JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumFractionDigits);
3554514f5e3Sopenharmony_ci        RETURN_IF_ABRUPT_COMPLETION(thread);
3564514f5e3Sopenharmony_ci    }
3574514f5e3Sopenharmony_ci
3584514f5e3Sopenharmony_ci    // 5. Let pluralCategories be a List of Strings representing the possible results of PluralRuleSelect
3594514f5e3Sopenharmony_ci    // for the selected locale pr.[[Locale]]. This List consists of unique String values,
3604514f5e3Sopenharmony_ci    // from the the list "zero", "one", "two", "few", "many" and "other",
3614514f5e3Sopenharmony_ci    // that are relevant for the locale whose localization is specified in LDML Language Plural Rules.
3624514f5e3Sopenharmony_ci    UErrorCode status = U_ZERO_ERROR;
3634514f5e3Sopenharmony_ci    icu::PluralRules *icuPluralRules = pluralRules->GetIcuPluralRules();
3644514f5e3Sopenharmony_ci    ASSERT(icuPluralRules != nullptr);
3654514f5e3Sopenharmony_ci    std::unique_ptr<icu::StringEnumeration> categories(icuPluralRules->getKeywords(status));
3664514f5e3Sopenharmony_ci    int32_t count = categories->count(status);
3674514f5e3Sopenharmony_ci    ASSERT(U_SUCCESS(status));
3684514f5e3Sopenharmony_ci    JSHandle<TaggedArray> pluralCategories = factory->NewTaggedArray(count);
3694514f5e3Sopenharmony_ci    for (int32_t i = 0; i < count; i++) {
3704514f5e3Sopenharmony_ci        const icu::UnicodeString *category = categories->snext(status);
3714514f5e3Sopenharmony_ci        ASSERT(U_SUCCESS(status));
3724514f5e3Sopenharmony_ci        JSHandle<EcmaString> value = intl::LocaleHelper::UStringToString(thread, *category);
3734514f5e3Sopenharmony_ci        pluralCategories->Set(thread, i, value);
3744514f5e3Sopenharmony_ci    }
3754514f5e3Sopenharmony_ci
3764514f5e3Sopenharmony_ci    // 6. Perform ! CreateDataProperty(options, "pluralCategories", CreateArrayFromList(pluralCategories)).
3774514f5e3Sopenharmony_ci    property = globalConst->GetHandledPluralCategoriesString();
3784514f5e3Sopenharmony_ci    JSHandle<JSArray> jsPluralCategories = JSArray::CreateArrayFromList(thread, pluralCategories);
3794514f5e3Sopenharmony_ci    JSObject::CreateDataPropertyOrThrow(thread, options, property, JSHandle<JSTaggedValue>::Cast(jsPluralCategories));
3804514f5e3Sopenharmony_ci    RETURN_IF_ABRUPT_COMPLETION(thread);
3814514f5e3Sopenharmony_ci}
3824514f5e3Sopenharmony_ci}  // namespace panda::ecmascript
383