1 /*
2  * Copyright (c) 2021-2022 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 #include "intl_addon.h"
17 
18 #include <vector>
19 #include <set>
20 #include "error_util.h"
21 #include "i18n_hilog.h"
22 #include "js_utils.h"
23 #include "node_api.h"
24 #include "utils.h"
25 
26 namespace OHOS {
27 namespace Global {
28 namespace I18n {
29 static thread_local napi_ref *g_constructor = nullptr;
30 
IntlAddon()31 IntlAddon::IntlAddon() : env_(nullptr) {}
32 
~IntlAddon()33 IntlAddon::~IntlAddon()
34 {
35 }
36 
Destructor(napi_env env, void *nativeObject, void *hint)37 void IntlAddon::Destructor(napi_env env, void *nativeObject, void *hint)
38 {
39     if (!nativeObject) {
40         return;
41     }
42     delete reinterpret_cast<IntlAddon *>(nativeObject);
43     nativeObject = nullptr;
44 }
45 
SetProperty(napi_env env, napi_callback_info info)46 napi_value IntlAddon::SetProperty(napi_env env, napi_callback_info info)
47 {
48     // do nothing but provided as an input parameter for DECLARE_NAPI_GETTER_SETTER;
49     napi_value result = nullptr;
50     NAPI_CALL(env, napi_get_undefined(env, &result));
51     return result;
52 }
53 
InitLocale(napi_env env, napi_value exports)54 napi_value IntlAddon::InitLocale(napi_env env, napi_value exports)
55 {
56     napi_status status = napi_ok;
57     napi_property_descriptor properties[] = {
58         DECLARE_NAPI_GETTER_SETTER("language", GetLanguage, SetProperty),
59         DECLARE_NAPI_GETTER_SETTER("baseName", GetBaseName, SetProperty),
60         DECLARE_NAPI_GETTER_SETTER("region", GetRegion, SetProperty),
61         DECLARE_NAPI_GETTER_SETTER("script", GetScript, SetProperty),
62         DECLARE_NAPI_GETTER_SETTER("calendar", GetCalendar, SetProperty),
63         DECLARE_NAPI_GETTER_SETTER("collation", GetCollation, SetProperty),
64         DECLARE_NAPI_GETTER_SETTER("hourCycle", GetHourCycle, SetProperty),
65         DECLARE_NAPI_GETTER_SETTER("numberingSystem", GetNumberingSystem, SetProperty),
66         DECLARE_NAPI_GETTER_SETTER("numeric", GetNumeric, SetProperty),
67         DECLARE_NAPI_GETTER_SETTER("caseFirst", GetCaseFirst, SetProperty),
68         DECLARE_NAPI_FUNCTION("toString", ToString),
69         DECLARE_NAPI_FUNCTION("minimize", Minimize),
70         DECLARE_NAPI_FUNCTION("maximize", Maximize),
71     };
72 
73     napi_value constructor = nullptr;
74     status = napi_define_class(env, "Locale", NAPI_AUTO_LENGTH, LocaleConstructor, nullptr,
75         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
76     if (status != napi_ok) {
77         HILOG_ERROR_I18N("Define class failed when InitLocale");
78         return nullptr;
79     }
80 
81     status = napi_set_named_property(env, exports, "Locale", constructor);
82     if (status != napi_ok) {
83         HILOG_ERROR_I18N("Set property failed when InitLocale");
84         return nullptr;
85     }
86     g_constructor = new (std::nothrow) napi_ref;
87     if (!g_constructor) {
88         HILOG_ERROR_I18N("Failed to create ref at init");
89         return nullptr;
90     }
91     status = napi_create_reference(env, constructor, 1, g_constructor);
92     if (status != napi_ok) {
93         HILOG_ERROR_I18N("Failed to create reference at init");
94         return nullptr;
95     }
96     return exports;
97 }
98 
InitDateTimeFormat(napi_env env, napi_value exports)99 napi_value IntlAddon::InitDateTimeFormat(napi_env env, napi_value exports)
100 {
101     napi_status status = napi_ok;
102     napi_property_descriptor properties[] = {
103         DECLARE_NAPI_FUNCTION("format", FormatDateTime),
104         DECLARE_NAPI_FUNCTION("formatRange", FormatDateTimeRange),
105         DECLARE_NAPI_FUNCTION("resolvedOptions", GetDateTimeResolvedOptions)
106     };
107 
108     napi_value constructor = nullptr;
109     status = napi_define_class(env, "DateTimeFormat", NAPI_AUTO_LENGTH, DateTimeFormatConstructor, nullptr,
110         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
111     if (status != napi_ok) {
112         HILOG_ERROR_I18N("Define class failed when InitDateTimeFormat");
113         return nullptr;
114     }
115 
116     status = napi_set_named_property(env, exports, "DateTimeFormat", constructor);
117     if (status != napi_ok) {
118         HILOG_ERROR_I18N("Set property failed when InitDateTimeFormat");
119         return nullptr;
120     }
121     return exports;
122 }
123 
InitRelativeTimeFormat(napi_env env, napi_value exports)124 napi_value IntlAddon::InitRelativeTimeFormat(napi_env env, napi_value exports)
125 {
126     napi_status status = napi_ok;
127     napi_property_descriptor properties[] = {
128         DECLARE_NAPI_FUNCTION("format", FormatRelativeTime),
129         DECLARE_NAPI_FUNCTION("formatToParts", FormatToParts),
130         DECLARE_NAPI_FUNCTION("resolvedOptions", GetRelativeTimeResolvedOptions)
131     };
132 
133     napi_value constructor = nullptr;
134     status = napi_define_class(env, "RelativeTimeFormat", NAPI_AUTO_LENGTH, RelativeTimeFormatConstructor, nullptr,
135         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
136     if (status != napi_ok) {
137         HILOG_ERROR_I18N("Define class failed when InitRelativeTimeFormat");
138         return nullptr;
139     }
140 
141     status = napi_set_named_property(env, exports, "RelativeTimeFormat", constructor);
142     if (status != napi_ok) {
143         HILOG_ERROR_I18N("Set property failed when InitRelativeTimeFormat");
144         return nullptr;
145     }
146     return exports;
147 }
148 
InitNumberFormat(napi_env env, napi_value exports)149 napi_value IntlAddon::InitNumberFormat(napi_env env, napi_value exports)
150 {
151     napi_status status = napi_ok;
152     napi_property_descriptor properties[] = {
153         DECLARE_NAPI_FUNCTION("format", FormatNumber),
154         DECLARE_NAPI_FUNCTION("resolvedOptions", GetNumberResolvedOptions)
155     };
156 
157     napi_value constructor = nullptr;
158     status = napi_define_class(env, "NumberFormat", NAPI_AUTO_LENGTH, NumberFormatConstructor, nullptr,
159         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
160     if (status != napi_ok) {
161         HILOG_ERROR_I18N("Define class failed when InitNumberFormat");
162         return nullptr;
163     }
164 
165     status = napi_set_named_property(env, exports, "NumberFormat", constructor);
166     if (status != napi_ok) {
167         HILOG_ERROR_I18N("Set property failed when InitNumberFormat");
168         return nullptr;
169     }
170     return exports;
171 }
172 
GetOptionValue(napi_env env, napi_value options, const std::string &optionName, std::map<std::string, std::string> &map)173 void GetOptionValue(napi_env env, napi_value options, const std::string &optionName,
174     std::map<std::string, std::string> &map)
175 {
176     napi_value optionValue = nullptr;
177     napi_valuetype type = napi_undefined;
178     napi_status status = napi_typeof(env, options, &type);
179     if (status != napi_ok && type != napi_object) {
180         HILOG_ERROR_I18N("Get option failed, option is not an object");
181         return;
182     }
183     bool hasProperty = false;
184     napi_status propStatus = napi_has_named_property(env, options, optionName.c_str(), &hasProperty);
185     if (propStatus == napi_ok && hasProperty) {
186         status = napi_get_named_property(env, options, optionName.c_str(), &optionValue);
187         if (status == napi_ok) {
188             size_t len = 0;
189             napi_get_value_string_utf8(env, optionValue, nullptr, 0, &len);
190             std::vector<char> optionBuf(len + 1);
191             status = napi_get_value_string_utf8(env, optionValue, optionBuf.data(), len + 1, &len);
192             if (status != napi_ok) {
193                 return;
194             }
195             map.insert(make_pair(optionName, optionBuf.data()));
196         }
197     }
198 }
199 
GetIntegerOptionValue(napi_env env, napi_value options, const std::string &optionName, std::map<std::string, std::string> &map)200 int64_t GetIntegerOptionValue(napi_env env, napi_value options, const std::string &optionName,
201     std::map<std::string, std::string> &map)
202 {
203     napi_value optionValue = nullptr;
204     int64_t integerValue = -1;
205     napi_valuetype type = napi_undefined;
206     napi_status status = napi_typeof(env, options, &type);
207     if (status != napi_ok && type != napi_object) {
208         HILOG_ERROR_I18N("GetIntegerOptionValue: Set option failed, option is not an object");
209         return integerValue;
210     }
211     bool hasProperty = false;
212     napi_status propStatus = napi_has_named_property(env, options, optionName.c_str(), &hasProperty);
213     if (propStatus == napi_ok && hasProperty) {
214         status = napi_get_named_property(env, options, optionName.c_str(), &optionValue);
215         if (status == napi_ok) {
216             status = napi_get_value_int64(env, optionValue, &integerValue);
217             if (status == napi_ok) {
218                 map.insert(make_pair(optionName, std::to_string(integerValue)));
219             }
220         }
221     }
222     return integerValue;
223 }
224 
GetBoolOptionValue(napi_env env, napi_value options, const std::string &optionName, std::map<std::string, std::string> &map)225 void GetBoolOptionValue(napi_env env, napi_value options, const std::string &optionName,
226     std::map<std::string, std::string> &map)
227 {
228     napi_value optionValue = nullptr;
229     napi_valuetype type = napi_undefined;
230     napi_status status = napi_typeof(env, options, &type);
231     if (status != napi_ok && type != napi_object) {
232         HILOG_ERROR_I18N("GetBoolOptionValue: Set option failed, option is not an object");
233         return;
234     }
235     bool hasProperty = false;
236     napi_status propStatus = napi_has_named_property(env, options, optionName.c_str(), &hasProperty);
237     if (propStatus == napi_ok && hasProperty) {
238         status = napi_get_named_property(env, options, optionName.c_str(), &optionValue);
239         if (status == napi_ok) {
240             bool boolValue = false;
241             napi_get_value_bool(env, optionValue, &boolValue);
242             std::string value = boolValue ? "true" : "false";
243             map.insert(make_pair(optionName, value));
244         }
245     }
246 }
247 
GetDateOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)248 void GetDateOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
249 {
250     GetOptionValue(env, options, "calendar", map);
251     GetOptionValue(env, options, "dateStyle", map);
252     GetOptionValue(env, options, "timeStyle", map);
253     GetOptionValue(env, options, "hourCycle", map);
254     GetOptionValue(env, options, "timeZone", map);
255     GetOptionValue(env, options, "timeZoneName", map);
256     GetOptionValue(env, options, "numberingSystem", map);
257     GetBoolOptionValue(env, options, "hour12", map);
258     GetOptionValue(env, options, "weekday", map);
259     GetOptionValue(env, options, "era", map);
260     GetOptionValue(env, options, "year", map);
261     GetOptionValue(env, options, "month", map);
262     GetOptionValue(env, options, "day", map);
263     GetOptionValue(env, options, "hour", map);
264     GetOptionValue(env, options, "minute", map);
265     GetOptionValue(env, options, "second", map);
266     GetOptionValue(env, options, "localeMatcher", map);
267     GetOptionValue(env, options, "formatMatcher", map);
268     GetOptionValue(env, options, "dayPeriod", map);
269 }
270 
GetRelativeTimeOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)271 void GetRelativeTimeOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
272 {
273     GetOptionValue(env, options, "localeMatcher", map);
274     GetOptionValue(env, options, "numeric", map);
275     GetOptionValue(env, options, "style", map);
276 }
277 
GetLocaleTag(napi_env env, napi_value argv)278 std::string GetLocaleTag(napi_env env, napi_value argv)
279 {
280     std::string localeTag = "";
281     std::vector<char> buf;
282     if (argv != nullptr) {
283         napi_valuetype valueType = napi_valuetype::napi_undefined;
284         napi_typeof(env, argv, &valueType);
285         if (valueType != napi_valuetype::napi_string) {
286             HILOG_ERROR_I18N("GetLocaleTag: Parameter type does not match");
287             return "";
288         }
289         size_t len = 0;
290         napi_status status = napi_get_value_string_utf8(env, argv, nullptr, 0, &len);
291         if (status != napi_ok) {
292             HILOG_ERROR_I18N("GetLocaleTag -> string: Get locale tag length failed");
293             return "";
294         }
295         buf.resize(len + 1);
296         status = napi_get_value_string_utf8(env, argv, buf.data(), len + 1, &len);
297         if (status != napi_ok) {
298             HILOG_ERROR_I18N("GetLocaleTag: Get locale tag failed");
299             return "";
300         }
301         localeTag = buf.data();
302     } else {
303         localeTag = "";
304     }
305     return localeTag;
306 }
307 
LocaleConstructor(napi_env env, napi_callback_info info)308 napi_value IntlAddon::LocaleConstructor(napi_env env, napi_callback_info info)
309 {
310     size_t argc = 2;
311     napi_value argv[2] = { nullptr };
312     napi_value thisVar = nullptr;
313     void *data = nullptr;
314     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
315     if (status != napi_ok) {
316         return nullptr;
317     }
318     std::string localeTag = GetLocaleTag(env, argc > 0 ? argv[0] : nullptr);
319 
320     std::map<std::string, std::string> map = {};
321     if (argc > 1) {
322         GetOptionValue(env, argv[1], "calendar", map);
323         GetOptionValue(env, argv[1], "collation", map);
324         GetOptionValue(env, argv[1], "hourCycle", map);
325         GetOptionValue(env, argv[1], "numberingSystem", map);
326         GetBoolOptionValue(env, argv[1], "numeric", map);
327         GetOptionValue(env, argv[1], "caseFirst", map);
328     }
329     std::unique_ptr<IntlAddon> obj = nullptr;
330     obj = std::make_unique<IntlAddon>();
331     status =
332         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
333     if (status != napi_ok) {
334         HILOG_ERROR_I18N("LocaleConstructor: Wrap IntlAddon failed");
335         return nullptr;
336     }
337     if (!obj->InitLocaleContext(env, info, localeTag, map)) {
338         return nullptr;
339     }
340     obj.release();
341     return thisVar;
342 }
343 
InitLocaleContext(napi_env env, napi_callback_info info, const std::string localeTag, std::map<std::string, std::string> &map)344 bool IntlAddon::InitLocaleContext(napi_env env, napi_callback_info info, const std::string localeTag,
345     std::map<std::string, std::string> &map)
346 {
347     napi_value global = nullptr;
348     napi_status status = napi_get_global(env, &global);
349     if (status != napi_ok) {
350         HILOG_ERROR_I18N("InitLocaleContext: Get global failed");
351         return false;
352     }
353     env_ = env;
354     locale_ = std::make_unique<LocaleInfo>(localeTag, map);
355 
356     return locale_ != nullptr;
357 }
358 
GetLocaleTags(napi_env env, napi_value rawLocaleTag, std::vector<std::string> &localeTags)359 void GetLocaleTags(napi_env env, napi_value rawLocaleTag, std::vector<std::string> &localeTags)
360 {
361     size_t len = 0;
362     napi_status status = napi_get_value_string_utf8(env, rawLocaleTag, nullptr, 0, &len);
363     if (status != napi_ok) {
364         HILOG_ERROR_I18N("GetLocaleTag -> void: Get locale tag length failed");
365         return;
366     }
367     std::vector<char> buf(len + 1);
368     status = napi_get_value_string_utf8(env, rawLocaleTag, buf.data(), len + 1, &len);
369     if (status != napi_ok) {
370         HILOG_ERROR_I18N("GetLocaleTags: Get locale tag failed");
371         return;
372     }
373     localeTags.push_back(buf.data());
374 }
375 
DateTimeFormatConstructor(napi_env env, napi_callback_info info)376 napi_value IntlAddon::DateTimeFormatConstructor(napi_env env, napi_callback_info info)
377 {
378     size_t argc = 2;
379     napi_value argv[2] = { nullptr };
380     napi_value thisVar = nullptr;
381     void *data = nullptr;
382     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
383     if (status != napi_ok) {
384         return nullptr;
385     }
386     std::vector<std::string> localeTags;
387     if (argc > 0) {
388         napi_valuetype valueType = napi_valuetype::napi_undefined;
389         napi_typeof(env, argv[0], &valueType);
390         bool isArray = false;
391         napi_is_array(env, argv[0], &isArray);
392         if (valueType == napi_valuetype::napi_string) {
393             GetLocaleTags(env, argv[0], localeTags);
394         } else if (isArray) {
395             uint32_t arrayLength = 0;
396             napi_get_array_length(env, argv[0], &arrayLength);
397             napi_value element = nullptr;
398             for (uint32_t i = 0; i < arrayLength; i++) {
399                 napi_get_element(env, argv[0], i, &element);
400                 GetLocaleTags(env, element, localeTags);
401             }
402         }
403     }
404     std::map<std::string, std::string> map = {};
405     if (argc > 1) {
406         GetDateOptionValues(env, argv[1], map);
407     }
408     std::unique_ptr<IntlAddon> obj = nullptr;
409     obj = std::make_unique<IntlAddon>();
410     status =
411         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
412     if (status != napi_ok) {
413         HILOG_ERROR_I18N("DateTimeFormatConstructor: Wrap IntlAddon failed");
414         return nullptr;
415     }
416     if (!obj->InitDateTimeFormatContext(env, info, localeTags, map)) {
417         HILOG_ERROR_I18N("DateTimeFormatConstructor: Init DateTimeFormat failed");
418         return nullptr;
419     }
420     obj.release();
421     return thisVar;
422 }
423 
InitDateTimeFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags, std::map<std::string, std::string> &map)424 bool IntlAddon::InitDateTimeFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
425     std::map<std::string, std::string> &map)
426 {
427     napi_value global = nullptr;
428     napi_status status = napi_get_global(env, &global);
429     if (status != napi_ok) {
430         HILOG_ERROR_I18N("InitDateTimeFormatContext: Get global failed");
431         return false;
432     }
433     env_ = env;
434     datefmt_ = DateTimeFormat::CreateInstance(localeTags, map);
435 
436     return datefmt_ != nullptr;
437 }
438 
RelativeTimeFormatConstructor(napi_env env, napi_callback_info info)439 napi_value IntlAddon::RelativeTimeFormatConstructor(napi_env env, napi_callback_info info)
440 {
441     size_t argc = 2;
442     napi_value argv[2] = { nullptr };
443     napi_value thisVar = nullptr;
444     void *data = nullptr;
445     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
446     if (status != napi_ok) {
447         return nullptr;
448     }
449     std::vector<std::string> localeTags;
450     if (argc > 0) {
451         napi_valuetype valueType = napi_valuetype::napi_undefined;
452         napi_typeof(env, argv[0], &valueType);
453         bool isArray = false;
454         napi_is_array(env, argv[0], &isArray);
455         if (valueType == napi_valuetype::napi_string) {
456             GetLocaleTags(env, argv[0], localeTags);
457         } else if (isArray) {
458             uint32_t arrayLength = 0;
459             napi_get_array_length(env, argv[0], &arrayLength);
460             napi_value element = nullptr;
461             for (uint32_t i = 0; i < arrayLength; i++) {
462                 napi_get_element(env, argv[0], i, &element);
463                 GetLocaleTags(env, element, localeTags);
464             }
465         }
466     }
467     std::map<std::string, std::string> map = {};
468     if (argc > 1) {
469         GetRelativeTimeOptionValues(env, argv[1], map);
470     }
471     std::unique_ptr<IntlAddon> obj = nullptr;
472     obj = std::make_unique<IntlAddon>();
473     status =
474         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
475     if (status != napi_ok) {
476         HILOG_ERROR_I18N("RelativeTimeFormatConstructor: Wrap IntlAddon failed");
477         return nullptr;
478     }
479     if (!obj->InitRelativeTimeFormatContext(env, info, localeTags, map)) {
480         HILOG_ERROR_I18N("Init RelativeTimeFormat failed");
481         return nullptr;
482     }
483     obj.release();
484     return thisVar;
485 }
486 
InitRelativeTimeFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags, std::map<std::string, std::string> &map)487 bool IntlAddon::InitRelativeTimeFormatContext(napi_env env, napi_callback_info info,
488     std::vector<std::string> localeTags, std::map<std::string, std::string> &map)
489 {
490     env_ = env;
491     relativetimefmt_ = std::make_unique<RelativeTimeFormat>(localeTags, map);
492 
493     return relativetimefmt_ != nullptr;
494 }
495 
FormatDateTime(napi_env env, napi_callback_info info)496 napi_value IntlAddon::FormatDateTime(napi_env env, napi_callback_info info)
497 {
498     size_t argc = 1;
499     napi_value argv[1] = { 0 };
500     napi_value thisVar = nullptr;
501     void *data = nullptr;
502     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
503 
504     int64_t milliseconds = GetMilliseconds(env, argv, 0);
505     if (milliseconds == -1) {
506         return nullptr;
507     }
508     IntlAddon *obj = nullptr;
509     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
510     if (status != napi_ok || !obj || !obj->datefmt_) {
511         HILOG_ERROR_I18N("FormatDateTime: Get DateTimeFormat object failed");
512         return nullptr;
513     }
514     std::string value = obj->datefmt_->Format(milliseconds);
515     napi_value result = nullptr;
516     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
517     if (status != napi_ok) {
518         HILOG_ERROR_I18N("FormatDateTime: Create format string failed");
519         return nullptr;
520     }
521     return result;
522 }
523 
FormatDateTimeRange(napi_env env, napi_callback_info info)524 napi_value IntlAddon::FormatDateTimeRange(napi_env env, napi_callback_info info)
525 {
526     size_t argc = 2;
527     napi_value argv[2] = { nullptr };
528     napi_value thisVar = nullptr;
529     void *data = nullptr;
530     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
531     if (argc < FUNC_ARGS_COUNT) {
532         HILOG_ERROR_I18N("Parameter wrong");
533         return nullptr;
534     }
535     int64_t firstMilliseconds = GetMilliseconds(env, argv, 0);
536     int64_t secondMilliseconds = GetMilliseconds(env, argv, 1);
537     if (firstMilliseconds == -1 || secondMilliseconds == -1) {
538         return nullptr;
539     }
540     IntlAddon *obj = nullptr;
541     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
542     if (status != napi_ok || !obj || !obj->datefmt_) {
543         HILOG_ERROR_I18N("FormatDateTimeRange: Get DateTimeFormat object failed");
544         return nullptr;
545     }
546     std::string value = obj->datefmt_->FormatRange(firstMilliseconds, secondMilliseconds);
547     napi_value result = nullptr;
548     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
549     if (status != napi_ok) {
550         HILOG_ERROR_I18N("FormatDateTimeRange: Create format string failed");
551         return nullptr;
552     }
553     return result;
554 }
555 
GetNumberOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)556 void GetNumberOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
557 {
558     GetOptionValue(env, options, "currency", map);
559     GetOptionValue(env, options, "currencySign", map);
560     GetOptionValue(env, options, "currencyDisplay", map);
561     GetOptionValue(env, options, "unit", map);
562     GetOptionValue(env, options, "unitDisplay", map);
563     GetOptionValue(env, options, "compactDisplay", map);
564     GetOptionValue(env, options, "signDisplay", map);
565     GetOptionValue(env, options, "localeMatcher", map);
566     GetOptionValue(env, options, "style", map);
567     GetOptionValue(env, options, "numberingSystem", map);
568     GetOptionValue(env, options, "notation", map);
569     GetOptionValue(env, options, "unitUsage", map);
570     GetBoolOptionValue(env, options, "useGrouping", map);
571     GetIntegerOptionValue(env, options, "minimumIntegerDigits", map);
572     int64_t minFd = GetIntegerOptionValue(env, options, "minimumFractionDigits", map);
573     int64_t maxFd = GetIntegerOptionValue(env, options, "maximumFractionDigits", map);
574     if (minFd != -1 && maxFd != -1 && minFd > maxFd) {
575         HILOG_ERROR_I18N(
576             "GetNumberOptionValues: Invalid parameter value: minimumFractionDigits > maximumFractionDigits");
577     }
578     GetIntegerOptionValue(env, options, "minimumSignificantDigits", map);
579     GetIntegerOptionValue(env, options, "maximumSignificantDigits", map);
580 }
581 
NumberFormatConstructor(napi_env env, napi_callback_info info)582 napi_value IntlAddon::NumberFormatConstructor(napi_env env, napi_callback_info info)
583 {
584     size_t argc = 2;
585     napi_value argv[2] = { nullptr };
586     napi_value thisVar = nullptr;
587     void *data = nullptr;
588     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
589     if (status != napi_ok) {
590         return nullptr;
591     }
592     std::vector<std::string> localeTags;
593     if (argc > 0) {
594         napi_valuetype valueType = napi_valuetype::napi_undefined;
595         napi_typeof(env, argv[0], &valueType);
596         bool isArray = false;
597         napi_is_array(env, argv[0], &isArray);
598 
599         if (valueType == napi_valuetype::napi_string) {
600             GetLocaleTags(env, argv[0], localeTags);
601         } else if (isArray) {
602             uint32_t arrayLength = 0;
603             napi_get_array_length(env, argv[0], &arrayLength);
604             napi_value element = nullptr;
605             for (uint32_t i = 0; i < arrayLength; i++) {
606                 napi_get_element(env, argv[0], i, &element);
607                 GetLocaleTags(env, element, localeTags);
608             }
609         }
610     }
611     std::map<std::string, std::string> map = {};
612     if (argc > 1) {
613         GetNumberOptionValues(env, argv[1], map);
614     }
615     std::unique_ptr<IntlAddon> obj = nullptr;
616     obj = std::make_unique<IntlAddon>();
617     status =
618         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
619     if (status != napi_ok) {
620         HILOG_ERROR_I18N("NumberFormatConstructor: Wrap IntlAddon failed");
621         return nullptr;
622     }
623     if (!obj->InitNumberFormatContext(env, info, localeTags, map)) {
624         HILOG_ERROR_I18N("Init NumberFormat failed");
625         return nullptr;
626     }
627     obj.release();
628     return thisVar;
629 }
630 
InitNumberFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags, std::map<std::string, std::string> &map)631 bool IntlAddon::InitNumberFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
632     std::map<std::string, std::string> &map)
633 {
634     napi_value global = nullptr;
635     napi_status status = napi_get_global(env, &global);
636     if (status != napi_ok) {
637         HILOG_ERROR_I18N("InitNumberFormatContext: Get global failed");
638         return false;
639     }
640     env_ = env;
641     numberfmt_ = std::make_unique<NumberFormat>(localeTags, map);
642 
643     return numberfmt_ != nullptr;
644 }
645 
GetMilliseconds(napi_env env, napi_value *argv, int index)646 int64_t IntlAddon::GetMilliseconds(napi_env env, napi_value *argv, int index)
647 {
648     napi_value funcGetDateInfo = nullptr;
649     napi_status status = napi_get_named_property(env, argv[index], "getTime", &funcGetDateInfo);
650     if (status != napi_ok) {
651         HILOG_ERROR_I18N("Get Milliseconds property failed");
652         return -1;
653     }
654     napi_value ret_value = nullptr;
655     status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value);
656     if (status != napi_ok) {
657         HILOG_ERROR_I18N("Get Milliseconds function failed");
658         return -1;
659     }
660     int64_t milliseconds = 0;
661     status = napi_get_value_int64(env, ret_value, &milliseconds);
662     if (status != napi_ok) {
663         HILOG_ERROR_I18N("Get Milliseconds failed");
664         return -1;
665     }
666     return milliseconds;
667 }
668 
GetLanguage(napi_env env, napi_callback_info info)669 napi_value IntlAddon::GetLanguage(napi_env env, napi_callback_info info)
670 {
671     napi_value thisVar = nullptr;
672     void *data = nullptr;
673     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
674 
675     IntlAddon *obj = nullptr;
676     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
677     if (status != napi_ok || !obj || !obj->locale_) {
678         HILOG_ERROR_I18N("GetLanguage: Get Locale object failed");
679         return nullptr;
680     }
681     std::string value = obj->locale_->GetLanguage();
682 
683     napi_value result = nullptr;
684     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
685     if (status != napi_ok) {
686         HILOG_ERROR_I18N("GetLanguage: Create language string failed");
687         return nullptr;
688     }
689     return result;
690 }
691 
GetScript(napi_env env, napi_callback_info info)692 napi_value IntlAddon::GetScript(napi_env env, napi_callback_info info)
693 {
694     napi_value thisVar = nullptr;
695     void *data = nullptr;
696     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
697 
698     IntlAddon *obj = nullptr;
699     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
700     if (status != napi_ok || !obj || !obj->locale_) {
701         HILOG_ERROR_I18N("GetScript: Get Locale object failed");
702         return nullptr;
703     }
704     std::string value = obj->locale_->GetScript();
705 
706     napi_value result = nullptr;
707     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
708     if (status != napi_ok) {
709         HILOG_ERROR_I18N("Create script string failed");
710         return nullptr;
711     }
712     return result;
713 }
714 
GetRegion(napi_env env, napi_callback_info info)715 napi_value IntlAddon::GetRegion(napi_env env, napi_callback_info info)
716 {
717     napi_value thisVar = nullptr;
718     void *data = nullptr;
719     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
720 
721     IntlAddon *obj = nullptr;
722     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
723     if (status != napi_ok || !obj || !obj->locale_) {
724         HILOG_ERROR_I18N("GetRegion: Get Locale object failed");
725         return nullptr;
726     }
727     std::string value = obj->locale_->GetRegion();
728 
729     napi_value result = nullptr;
730     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
731     if (status != napi_ok) {
732         HILOG_ERROR_I18N("Create region string failed");
733         return nullptr;
734     }
735     return result;
736 }
737 
GetBaseName(napi_env env, napi_callback_info info)738 napi_value IntlAddon::GetBaseName(napi_env env, napi_callback_info info)
739 {
740     napi_value thisVar = nullptr;
741     void *data = nullptr;
742     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
743 
744     IntlAddon *obj = nullptr;
745     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
746     if (status != napi_ok || !obj || !obj->locale_) {
747         HILOG_ERROR_I18N("GetBaseName: Get Locale object failed");
748         return nullptr;
749     }
750     std::string value = obj->locale_->GetBaseName();
751 
752     napi_value result = nullptr;
753     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
754     if (status != napi_ok) {
755         HILOG_ERROR_I18N("GetBaseName: Create base name string failed");
756         return nullptr;
757     }
758     return result;
759 }
760 
GetCalendar(napi_env env, napi_callback_info info)761 napi_value IntlAddon::GetCalendar(napi_env env, napi_callback_info info)
762 {
763     napi_value thisVar = nullptr;
764     void *data = nullptr;
765     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
766 
767     IntlAddon *obj = nullptr;
768     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
769     if (status != napi_ok || !obj || !obj->locale_) {
770         HILOG_ERROR_I18N("GetCalendar: Get Locale object failed");
771         return nullptr;
772     }
773     std::string value = obj->locale_->GetCalendar();
774 
775     napi_value result = nullptr;
776     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
777     if (status != napi_ok) {
778         HILOG_ERROR_I18N("GetCalendar: Create base name string failed");
779         return nullptr;
780     }
781     return result;
782 }
783 
GetCollation(napi_env env, napi_callback_info info)784 napi_value IntlAddon::GetCollation(napi_env env, napi_callback_info info)
785 {
786     napi_value thisVar = nullptr;
787     void *data = nullptr;
788     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
789 
790     IntlAddon *obj = nullptr;
791     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
792     if (status != napi_ok || !obj || !obj->locale_) {
793         HILOG_ERROR_I18N("GetCollation: Get Locale object failed");
794         return nullptr;
795     }
796     std::string value = obj->locale_->GetCollation();
797 
798     napi_value result = nullptr;
799     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
800     if (status != napi_ok) {
801         HILOG_ERROR_I18N("GetCollation: Create base name string failed");
802         return nullptr;
803     }
804     return result;
805 }
806 
GetHourCycle(napi_env env, napi_callback_info info)807 napi_value IntlAddon::GetHourCycle(napi_env env, napi_callback_info info)
808 {
809     napi_value thisVar = nullptr;
810     void *data = nullptr;
811     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
812 
813     IntlAddon *obj = nullptr;
814     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
815     if (status != napi_ok || !obj || !obj->locale_) {
816         HILOG_ERROR_I18N("GetHourCycle: Get Locale object failed");
817         return nullptr;
818     }
819     std::string value = obj->locale_->GetHourCycle();
820 
821     napi_value result = nullptr;
822     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
823     if (status != napi_ok) {
824         HILOG_ERROR_I18N("GetHourCycle: Create base name string failed");
825         return nullptr;
826     }
827     return result;
828 }
829 
GetNumberingSystem(napi_env env, napi_callback_info info)830 napi_value IntlAddon::GetNumberingSystem(napi_env env, napi_callback_info info)
831 {
832     napi_value thisVar = nullptr;
833     void *data = nullptr;
834     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
835 
836     IntlAddon *obj = nullptr;
837     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
838     if (status != napi_ok || !obj || !obj->locale_) {
839         HILOG_ERROR_I18N("GetNumberingSystem: Get Locale object failed");
840         return nullptr;
841     }
842     std::string value = obj->locale_->GetNumberingSystem();
843 
844     napi_value result = nullptr;
845     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
846     if (status != napi_ok) {
847         HILOG_ERROR_I18N("GetNumberingSystem: Create base name string failed");
848         return nullptr;
849     }
850     return result;
851 }
852 
GetNumeric(napi_env env, napi_callback_info info)853 napi_value IntlAddon::GetNumeric(napi_env env, napi_callback_info info)
854 {
855     napi_value thisVar = nullptr;
856     void *data = nullptr;
857     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
858 
859     IntlAddon *obj = nullptr;
860     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
861     if (status != napi_ok || !obj || !obj->locale_) {
862         HILOG_ERROR_I18N("GetNumeric: Get Locale object failed");
863         return nullptr;
864     }
865     std::string value = obj->locale_->GetNumeric();
866     bool optionBoolValue = (value == "true");
867     napi_value result = nullptr;
868     status = napi_get_boolean(env, optionBoolValue, &result);
869     if (status != napi_ok) {
870         HILOG_ERROR_I18N("Create numeric boolean value failed");
871         return nullptr;
872     }
873     return result;
874 }
875 
GetCaseFirst(napi_env env, napi_callback_info info)876 napi_value IntlAddon::GetCaseFirst(napi_env env, napi_callback_info info)
877 {
878     napi_value thisVar = nullptr;
879     void *data = nullptr;
880     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
881 
882     IntlAddon *obj = nullptr;
883     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
884     if (status != napi_ok || !obj || !obj->locale_) {
885         HILOG_ERROR_I18N("GetCaseFirst: Get Locale object failed");
886         return nullptr;
887     }
888     std::string value = obj->locale_->GetCaseFirst();
889     napi_value result = nullptr;
890     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
891     if (status != napi_ok) {
892         HILOG_ERROR_I18N("Create caseFirst string failed");
893         return nullptr;
894     }
895     return result;
896 }
897 
ToString(napi_env env, napi_callback_info info)898 napi_value IntlAddon::ToString(napi_env env, napi_callback_info info)
899 {
900     napi_value thisVar = nullptr;
901     void *data = nullptr;
902     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
903 
904     IntlAddon *obj = nullptr;
905     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
906     if (status != napi_ok || !obj || !obj->locale_) {
907         HILOG_ERROR_I18N("ToString: Get Locale object failed");
908         return nullptr;
909     }
910     std::string value = obj->locale_->ToString();
911 
912     napi_value result = nullptr;
913     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
914     if (status != napi_ok) {
915         HILOG_ERROR_I18N("ToString: Create language string failed");
916         return nullptr;
917     }
918     return result;
919 }
920 
Maximize(napi_env env, napi_callback_info info)921 napi_value IntlAddon::Maximize(napi_env env, napi_callback_info info)
922 {
923     napi_value thisVar = nullptr;
924     void *data = nullptr;
925     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
926 
927     IntlAddon *obj = nullptr;
928     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
929     if (status != napi_ok || !obj || !obj->locale_) {
930         HILOG_ERROR_I18N("Maximize: Get Locale object failed");
931         return nullptr;
932     }
933     std::string localeTag = obj->locale_->Maximize();
934 
935     napi_value constructor = nullptr;
936     status = napi_get_reference_value(env, *g_constructor, &constructor);
937     if (status != napi_ok) {
938         HILOG_ERROR_I18N("Maximize: Get locale constructor reference failed");
939         return nullptr;
940     }
941     napi_value result = nullptr;
942     napi_value arg = nullptr;
943     status = napi_create_string_utf8(env, localeTag.c_str(), NAPI_AUTO_LENGTH, &arg);
944     if (status != napi_ok) {
945         HILOG_ERROR_I18N("Maximize: Create localeTag string failed");
946         return nullptr;
947     }
948     status = napi_new_instance(env, constructor, 1, &arg, &result);
949     if (status != napi_ok) {
950         HILOG_ERROR_I18N("Maximize: Create new locale instance failed");
951         return nullptr;
952     }
953     return result;
954 }
955 
Minimize(napi_env env, napi_callback_info info)956 napi_value IntlAddon::Minimize(napi_env env, napi_callback_info info)
957 {
958     napi_value thisVar = nullptr;
959     void *data = nullptr;
960     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
961 
962     IntlAddon *obj = nullptr;
963     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
964     if (status != napi_ok || !obj || !obj->locale_) {
965         HILOG_ERROR_I18N("Minimize: Get Locale object failed");
966         return nullptr;
967     }
968     std::string localeTag = obj->locale_->Minimize();
969 
970     napi_value constructor = nullptr;
971     status = napi_get_reference_value(env, *g_constructor, &constructor);
972     if (status != napi_ok) {
973         HILOG_ERROR_I18N("Minimize: Get locale constructor reference failed");
974         return nullptr;
975     }
976     napi_value result = nullptr;
977     napi_value arg = nullptr;
978     status = napi_create_string_utf8(env, localeTag.c_str(), NAPI_AUTO_LENGTH, &arg);
979     if (status != napi_ok) {
980         HILOG_ERROR_I18N("Minimize: Create localeTag string failed");
981         return nullptr;
982     }
983     status = napi_new_instance(env, constructor, 1, &arg, &result);
984     if (status != napi_ok) {
985         HILOG_ERROR_I18N("Minimize: Create new locale instance failed");
986         return nullptr;
987     }
988     return result;
989 }
990 
SetOptionProperties(napi_env env, napi_value &result, std::map<std::string, std::string> &options, const std::string &option)991 void SetOptionProperties(napi_env env, napi_value &result, std::map<std::string, std::string> &options,
992     const std::string &option)
993 {
994     if (options.count(option) > 0) {
995         std::string optionValue = options[option];
996         napi_value optionJsValue = nullptr;
997         napi_create_string_utf8(env, optionValue.c_str(), NAPI_AUTO_LENGTH, &optionJsValue);
998         napi_set_named_property(env, result, option.c_str(), optionJsValue);
999     } else {
1000         napi_value undefined = nullptr;
1001         napi_get_undefined(env, &undefined);
1002         napi_set_named_property(env, result, option.c_str(), undefined);
1003     }
1004 }
1005 
SetIntegerOptionProperties(napi_env env, napi_value &result, std::map<std::string, std::string> &options, const std::string &option)1006 void SetIntegerOptionProperties(napi_env env, napi_value &result, std::map<std::string, std::string> &options,
1007     const std::string &option)
1008 {
1009     if (options.count(option) > 0) {
1010         std::string optionValue = options[option];
1011         napi_value optionJsValue = nullptr;
1012         int32_t status = 0;
1013         int64_t integerValue = ConvertString2Int(optionValue, status);
1014         if (status != -1) {
1015             napi_create_int64(env, integerValue, &optionJsValue);
1016             napi_set_named_property(env, result, option.c_str(), optionJsValue);
1017             return;
1018         }
1019     }
1020     napi_value undefined = nullptr;
1021     napi_get_undefined(env, &undefined);
1022     napi_set_named_property(env, result, option.c_str(), undefined);
1023 }
1024 
SetBooleanOptionProperties(napi_env env, napi_value &result, std::map<std::string, std::string> &options, const std::string &option)1025 void SetBooleanOptionProperties(napi_env env, napi_value &result, std::map<std::string, std::string> &options,
1026     const std::string &option)
1027 {
1028     if (options.count(option) > 0) {
1029         std::string optionValue = options[option];
1030         bool optionBoolValue = (optionValue == "true");
1031         napi_value optionJsValue = nullptr;
1032         napi_get_boolean(env, optionBoolValue, &optionJsValue);
1033         napi_set_named_property(env, result, option.c_str(), optionJsValue);
1034     } else {
1035         napi_value undefined = nullptr;
1036         napi_get_undefined(env, &undefined);
1037         napi_set_named_property(env, result, option.c_str(), undefined);
1038     }
1039 }
1040 
GetRelativeTimeResolvedOptions(napi_env env, napi_callback_info info)1041 napi_value IntlAddon::GetRelativeTimeResolvedOptions(napi_env env, napi_callback_info info)
1042 {
1043     napi_value thisVar = nullptr;
1044     void *data = nullptr;
1045     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
1046 
1047     IntlAddon *obj = nullptr;
1048     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1049     if (status != napi_ok || !obj || !obj->relativetimefmt_) {
1050         HILOG_ERROR_I18N("GetRelativeTimeResolvedOptions: Get RelativeTimeFormat object failed");
1051         return nullptr;
1052     }
1053     napi_value result = nullptr;
1054     napi_create_object(env, &result);
1055     std::map<std::string, std::string> options = {};
1056     obj->relativetimefmt_->GetResolvedOptions(options);
1057     SetOptionProperties(env, result, options, "locale");
1058     SetOptionProperties(env, result, options, "style");
1059     SetOptionProperties(env, result, options, "numeric");
1060     SetOptionProperties(env, result, options, "numberingSystem");
1061     return result;
1062 }
1063 
GetDateTimeResolvedOptions(napi_env env, napi_callback_info info)1064 napi_value IntlAddon::GetDateTimeResolvedOptions(napi_env env, napi_callback_info info)
1065 {
1066     napi_value thisVar = nullptr;
1067     void *data = nullptr;
1068     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
1069 
1070     IntlAddon *obj = nullptr;
1071     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1072     if (status != napi_ok || !obj || !obj->datefmt_) {
1073         HILOG_ERROR_I18N("GetDateTimeResolvedOptions: Get DateTimeFormat object failed");
1074         return nullptr;
1075     }
1076     napi_value result = nullptr;
1077     napi_create_object(env, &result);
1078     std::map<std::string, std::string> options = {};
1079     obj->datefmt_->GetResolvedOptions(options);
1080     SetOptionProperties(env, result, options, "locale");
1081     SetOptionProperties(env, result, options, "calendar");
1082     SetOptionProperties(env, result, options, "dateStyle");
1083     SetOptionProperties(env, result, options, "timeStyle");
1084     SetOptionProperties(env, result, options, "hourCycle");
1085     SetOptionProperties(env, result, options, "timeZone");
1086     SetOptionProperties(env, result, options, "timeZoneName");
1087     SetOptionProperties(env, result, options, "numberingSystem");
1088     SetBooleanOptionProperties(env, result, options, "hour12");
1089     SetOptionProperties(env, result, options, "weekday");
1090     SetOptionProperties(env, result, options, "era");
1091     SetOptionProperties(env, result, options, "year");
1092     SetOptionProperties(env, result, options, "month");
1093     SetOptionProperties(env, result, options, "day");
1094     SetOptionProperties(env, result, options, "hour");
1095     SetOptionProperties(env, result, options, "minute");
1096     SetOptionProperties(env, result, options, "second");
1097     SetOptionProperties(env, result, options, "dayPeriod");
1098     SetOptionProperties(env, result, options, "localeMatcher");
1099     SetOptionProperties(env, result, options, "formatMatcher");
1100     return result;
1101 }
1102 
GetNumberResolvedOptions(napi_env env, napi_callback_info info)1103 napi_value IntlAddon::GetNumberResolvedOptions(napi_env env, napi_callback_info info)
1104 {
1105     napi_value thisVar = nullptr;
1106     void *data = nullptr;
1107     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
1108 
1109     IntlAddon *obj = nullptr;
1110     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1111     if (status != napi_ok || !obj || !obj->numberfmt_) {
1112         HILOG_ERROR_I18N("GetNumberResolvedOptions: Get NumberFormat object failed");
1113         return nullptr;
1114     }
1115     napi_value result = nullptr;
1116     napi_create_object(env, &result);
1117     std::map<std::string, std::string> options = {};
1118     obj->numberfmt_->GetResolvedOptions(options);
1119     SetOptionProperties(env, result, options, "locale");
1120     SetOptionProperties(env, result, options, "currency");
1121     SetOptionProperties(env, result, options, "currencySign");
1122     SetOptionProperties(env, result, options, "currencyDisplay");
1123     SetOptionProperties(env, result, options, "unit");
1124     SetOptionProperties(env, result, options, "unitDisplay");
1125     SetOptionProperties(env, result, options, "signDisplay");
1126     SetOptionProperties(env, result, options, "compactDisplay");
1127     SetOptionProperties(env, result, options, "notation");
1128     SetOptionProperties(env, result, options, "style");
1129     SetOptionProperties(env, result, options, "numberingSystem");
1130     SetOptionProperties(env, result, options, "unitUsage");
1131     SetBooleanOptionProperties(env, result, options, "useGrouping");
1132     SetIntegerOptionProperties(env, result, options, "minimumIntegerDigits");
1133     SetIntegerOptionProperties(env, result, options, "minimumFractionDigits");
1134     SetIntegerOptionProperties(env, result, options, "maximumFractionDigits");
1135     SetIntegerOptionProperties(env, result, options, "minimumSignificantDigits");
1136     SetIntegerOptionProperties(env, result, options, "maximumSignificantDigits");
1137     SetOptionProperties(env, result, options, "localeMatcher");
1138     return result;
1139 }
1140 
FormatNumber(napi_env env, napi_callback_info info)1141 napi_value IntlAddon::FormatNumber(napi_env env, napi_callback_info info)
1142 {
1143     size_t argc = 1;
1144     napi_value argv[1] = { 0 };
1145     napi_value thisVar = nullptr;
1146     void *data = nullptr;
1147     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1148     double number = 0;
1149     napi_get_value_double(env, argv[0], &number);
1150     IntlAddon *obj = nullptr;
1151     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1152     if (status != napi_ok || !obj || !obj->numberfmt_) {
1153         HILOG_ERROR_I18N("FormatNumber: Get NumberFormat object failed");
1154         return nullptr;
1155     }
1156     std::string value = obj->numberfmt_->Format(number);
1157     napi_value result = nullptr;
1158     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
1159     if (status != napi_ok) {
1160         HILOG_ERROR_I18N("FormatNumber: Create format string failed");
1161         return nullptr;
1162     }
1163     return result;
1164 }
1165 
GetCollatorLocaleMatcher(napi_env env, napi_value options, std::map<std::string, std::string> &map)1166 void GetCollatorLocaleMatcher(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1167 {
1168     GetOptionValue(env, options, "localeMatcher", map);
1169     auto it = map.find("localeMatcher");
1170     if (it != map.end()) {
1171         std::string localeMatcher = it->second;
1172         if (localeMatcher != "lookup" && localeMatcher != "best fit") {
1173             HILOG_ERROR_I18N("invalid localeMatcher");
1174             return;
1175         }
1176     } else {
1177         map.insert(std::make_pair("localeMatcher", "best fit"));
1178     }
1179 }
1180 
GetCollatorUsage(napi_env env, napi_value options, std::map<std::string, std::string> &map)1181 void GetCollatorUsage(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1182 {
1183     GetOptionValue(env, options, "usage", map);
1184     auto it = map.find("usage");
1185     if (it != map.end()) {
1186         std::string usage = it->second;
1187         if (usage != "sort" && usage != "search") {
1188             HILOG_ERROR_I18N("invalid usage");
1189             return;
1190         }
1191     } else {
1192         map.insert(std::make_pair("usage", "sort"));
1193     }
1194 }
1195 
GetCollatorSensitivity(napi_env env, napi_value options, std::map<std::string, std::string> &map)1196 void GetCollatorSensitivity(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1197 {
1198     GetOptionValue(env, options, "sensitivity", map);
1199     auto it = map.find("sensitivity");
1200     if (it != map.end()) {
1201         std::string sensitivity = it->second;
1202         if (sensitivity != "base" && sensitivity != "accent" && sensitivity != "case" && sensitivity != "variant") {
1203             HILOG_ERROR_I18N("invalid sensitivity");
1204             return;
1205         }
1206     } else {
1207         map.insert(std::make_pair("sensitivity", "variant"));
1208     }
1209 }
1210 
GetCollatorIgnorePunctuation(napi_env env, napi_value options, std::map<std::string, std::string> &map)1211 void GetCollatorIgnorePunctuation(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1212 {
1213     GetBoolOptionValue(env, options, "ignorePunctuation", map);
1214     auto it = map.find("ignorePunctuation");
1215     if (it != map.end()) {
1216         std::string ignorePunctuation = it->second;
1217         if (ignorePunctuation != "true" && ignorePunctuation != "false") {
1218             HILOG_ERROR_I18N("invalid ignorePunctuation");
1219             return;
1220         }
1221     } else {
1222         map.insert(std::make_pair("ignorePunctuation", "false"));
1223     }
1224 }
1225 
GetCollatorNumeric(napi_env env, napi_value options, std::map<std::string, std::string> &map)1226 void GetCollatorNumeric(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1227 {
1228     GetBoolOptionValue(env, options, "numeric", map);
1229     auto it = map.find("numeric");
1230     if (it != map.end()) {
1231         std::string numeric = it->second;
1232         if (numeric != "true" && numeric != "false") {
1233             HILOG_ERROR_I18N("invalid numeric");
1234             return;
1235         }
1236     }
1237 }
1238 
GetCollatorCaseFirst(napi_env env, napi_value options, std::map<std::string, std::string> &map)1239 void GetCollatorCaseFirst(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1240 {
1241     GetOptionValue(env, options, "caseFirst", map);
1242     auto it = map.find("caseFirst");
1243     if (it != map.end()) {
1244         std::string caseFirst = it->second;
1245         if (caseFirst != "upper" && caseFirst != "lower" && caseFirst != "false") {
1246             HILOG_ERROR_I18N("invalid caseFirst");
1247             return;
1248         }
1249     }
1250 }
1251 
GetCollatorCollation(napi_env env, napi_value options, std::map<std::string, std::string> &map)1252 void GetCollatorCollation(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1253 {
1254     GetOptionValue(env, options, "collation", map);
1255     auto it = map.find("collation");
1256     if (it != map.end()) {
1257         std::string collation = it->second;
1258         std::set<std::string> validCollation;
1259         validCollation.insert("big5han");
1260         validCollation.insert("compat");
1261         validCollation.insert("dict");
1262         validCollation.insert("direct");
1263         validCollation.insert("ducet");
1264         validCollation.insert("eor");
1265         validCollation.insert("gb2312");
1266         validCollation.insert("phonebk");
1267         validCollation.insert("phonetic");
1268         validCollation.insert("pinyin");
1269         validCollation.insert("reformed");
1270         validCollation.insert("searchjl");
1271         validCollation.insert("stroke");
1272         validCollation.insert("trad");
1273         validCollation.insert("unihan");
1274         validCollation.insert("zhuyin");
1275         if (validCollation.find(collation) == validCollation.end()) {
1276             map["collation"] = "default";
1277         }
1278     }
1279 }
1280 
GetCollatorOptionValue(napi_env env, napi_value options, std::map<std::string, std::string> &map)1281 void GetCollatorOptionValue(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1282 {
1283     GetCollatorLocaleMatcher(env, options, map);
1284     GetCollatorUsage(env, options, map);
1285     GetCollatorSensitivity(env, options, map);
1286     GetCollatorIgnorePunctuation(env, options, map);
1287     GetCollatorNumeric(env, options, map);
1288     GetCollatorCaseFirst(env, options, map);
1289     GetCollatorCollation(env, options, map);
1290 }
1291 
InitCollator(napi_env env, napi_value exports)1292 napi_value IntlAddon::InitCollator(napi_env env, napi_value exports)
1293 {
1294     napi_status status = napi_ok;
1295     napi_property_descriptor properties[] = {
1296         DECLARE_NAPI_FUNCTION("compare", CompareString),
1297         DECLARE_NAPI_FUNCTION("resolvedOptions", GetCollatorResolvedOptions)
1298     };
1299 
1300     napi_value constructor;
1301     status = napi_define_class(env, "Collator", NAPI_AUTO_LENGTH, CollatorConstructor, nullptr,
1302         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
1303     if (status != napi_ok) {
1304         HILOG_ERROR_I18N("Define class failed when InitCollator");
1305         return nullptr;
1306     }
1307 
1308     status = napi_set_named_property(env, exports, "Collator", constructor);
1309     if (status != napi_ok) {
1310         HILOG_ERROR_I18N("Set property failed when InitCollator");
1311         return nullptr;
1312     }
1313     return exports;
1314 }
1315 
CollatorConstructor(napi_env env, napi_callback_info info)1316 napi_value IntlAddon::CollatorConstructor(napi_env env, napi_callback_info info)
1317 {
1318     size_t argc = 2;
1319     napi_value argv[2] = { nullptr };
1320     napi_value thisVar = nullptr;
1321     void *data = nullptr;
1322     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1323     if (status != napi_ok) {
1324         return nullptr;
1325     }
1326     std::vector<std::string> localeTags;
1327     if (argc > 0) {
1328         napi_valuetype valueType = napi_valuetype::napi_undefined;
1329         napi_typeof(env, argv[0], &valueType);
1330         bool isArray = false;
1331         napi_is_array(env, argv[0], &isArray);
1332         if (valueType == napi_valuetype::napi_string) {
1333             GetLocaleTags(env, argv[0], localeTags);
1334         } else if (isArray) {
1335             uint32_t arrayLength = 0;
1336             napi_get_array_length(env, argv[0], &arrayLength);
1337             napi_value element = nullptr;
1338             for (uint32_t i = 0; i < arrayLength; i++) {
1339                 napi_get_element(env, argv[0], i, &element);
1340                 GetLocaleTags(env, element, localeTags);
1341             }
1342         }
1343     }
1344     std::map<std::string, std::string> map = {};
1345     if (argc > 1) {
1346         GetCollatorOptionValue(env, argv[1], map);
1347     }
1348     std::unique_ptr<IntlAddon> obj = nullptr;
1349     obj = std::make_unique<IntlAddon>();
1350     status =
1351         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
1352     if (status != napi_ok) {
1353         HILOG_ERROR_I18N("CollatorConstructor: Wrap IntlAddon failed");
1354         return nullptr;
1355     }
1356     if (!obj->InitCollatorContext(env, info, localeTags, map)) {
1357         HILOG_ERROR_I18N("CollatorConstructor: Init DateTimeFormat failed");
1358         return nullptr;
1359     }
1360     obj.release();
1361     return thisVar;
1362 }
1363 
InitCollatorContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags, std::map<std::string, std::string> &map)1364 bool IntlAddon::InitCollatorContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
1365     std::map<std::string, std::string> &map)
1366 {
1367     napi_value global = nullptr;
1368     napi_status status = napi_get_global(env, &global);
1369     if (status != napi_ok) {
1370         HILOG_ERROR_I18N("InitCollatorContext: Get global failed");
1371         return false;
1372     }
1373     env_ = env;
1374     collator_ = std::make_unique<Collator>(localeTags, map);
1375 
1376     return collator_ != nullptr;
1377 }
1378 
GetStringParameter(napi_env env, napi_value value, std::vector<char> &buf)1379 bool GetStringParameter(napi_env env, napi_value value, std::vector<char> &buf)
1380 {
1381     napi_valuetype valueType = napi_valuetype::napi_undefined;
1382     napi_typeof(env, value, &valueType);
1383     if (valueType != napi_valuetype::napi_string) {
1384         HILOG_ERROR_I18N("Parameter type does not match");
1385         return false;
1386     }
1387     size_t len = 0;
1388     napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
1389     if (status != napi_ok) {
1390         HILOG_ERROR_I18N("Get first length failed");
1391         return false;
1392     }
1393     buf.resize(len + 1);
1394     status = napi_get_value_string_utf8(env, value, buf.data(), len + 1, &len);
1395     if (status != napi_ok) {
1396         HILOG_ERROR_I18N("Get first failed");
1397         return false;
1398     }
1399 
1400     return true;
1401 }
1402 
FormatRelativeTime(napi_env env, napi_callback_info info)1403 napi_value IntlAddon::FormatRelativeTime(napi_env env, napi_callback_info info)
1404 {
1405     size_t argc = 2;
1406     napi_value argv[2] = { 0 };
1407     napi_value thisVar = nullptr;
1408     void *data = nullptr;
1409     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1410     napi_status status;
1411     double number;
1412     status = napi_get_value_double(env, argv[0], &number);
1413     if (status != napi_ok) {
1414         HILOG_ERROR_I18N("FormatRelativeTime: Get number failed");
1415         return nullptr;
1416     }
1417     std::vector<char> unit;
1418     if (!GetStringParameter(env, argv[1], unit)) {
1419         return nullptr;
1420     }
1421     IntlAddon *obj = nullptr;
1422     status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1423     if (status != napi_ok || !obj || !obj->relativetimefmt_) {
1424         HILOG_ERROR_I18N("FormatRelativeTime: Get RelativeTimeFormat object failed");
1425         return nullptr;
1426     }
1427     std::string value = obj->relativetimefmt_->Format(number, unit.data());
1428     napi_value result = nullptr;
1429     status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
1430     if (status != napi_ok) {
1431         HILOG_ERROR_I18N("FormatRelativeTime: Create format string failed");
1432         return nullptr;
1433     }
1434     return result;
1435 }
1436 
FillInArrayElement(napi_env env, napi_value &result, napi_status &status, const std::vector<std::vector<std::string>> &timeVector)1437 void IntlAddon::FillInArrayElement(napi_env env, napi_value &result, napi_status &status,
1438     const std::vector<std::vector<std::string>> &timeVector)
1439 {
1440     for (size_t i = 0; i < timeVector.size(); i++) {
1441         napi_value value = nullptr;
1442         status = napi_create_string_utf8(env, timeVector[i][1].c_str(), NAPI_AUTO_LENGTH, &value);
1443         if (status != napi_ok) {
1444             HILOG_ERROR_I18N("Failed to create string item imeVector[i][1].");
1445             return;
1446         }
1447         napi_value type = nullptr;
1448         status = napi_create_string_utf8(env, timeVector[i][0].c_str(), NAPI_AUTO_LENGTH, &type);
1449         if (status != napi_ok) {
1450             HILOG_ERROR_I18N("Failed to create string item timeVector[i][0].");
1451             return;
1452         }
1453         napi_value unit = nullptr;
1454         size_t unitIndex = 2;
1455         if (timeVector[i].size() > unitIndex) {
1456             status = napi_create_string_utf8(env, timeVector[i][unitIndex].c_str(), NAPI_AUTO_LENGTH, &unit);
1457             if (status != napi_ok) {
1458                 HILOG_ERROR_I18N("Failed to create string item timeVector[i][unitIndex].");
1459                 return;
1460             }
1461         } else {
1462             napi_get_undefined(env, &unit);
1463         }
1464         napi_value formatInfo;
1465         status = napi_create_object(env, &formatInfo);
1466         if (status != napi_ok) {
1467             HILOG_ERROR_I18N("Failed to create format info object.");
1468             return;
1469         }
1470         napi_set_named_property(env, formatInfo, "type", type);
1471         napi_set_named_property(env, formatInfo, "value", value);
1472         napi_set_named_property(env, formatInfo, "unit", unit);
1473         status = napi_set_element(env, result, i, formatInfo);
1474         if (status != napi_ok) {
1475             HILOG_ERROR_I18N("Failed to set array item");
1476             return;
1477         }
1478     }
1479 }
1480 
FormatToParts(napi_env env, napi_callback_info info)1481 napi_value IntlAddon::FormatToParts(napi_env env, napi_callback_info info)
1482 {
1483     size_t argc = 2;
1484     napi_value argv[2] = { 0 };
1485     napi_value thisVar = nullptr;
1486     void *data = nullptr;
1487     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1488     double number = 0;
1489     napi_get_value_double(env, argv[0], &number);
1490     std::vector<char> unit;
1491     if (!GetStringParameter(env, argv[1], unit)) {
1492         return nullptr;
1493     }
1494     IntlAddon *obj = nullptr;
1495     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1496     if (status != napi_ok || !obj || !obj->relativetimefmt_) {
1497         HILOG_ERROR_I18N("FormatToParts: Get RelativeTimeFormat object failed");
1498         return nullptr;
1499     }
1500     std::vector<std::vector<std::string>> timeVector;
1501     obj->relativetimefmt_->FormatToParts(number, unit.data(), timeVector);
1502     napi_value result = nullptr;
1503     status = napi_create_array_with_length(env, timeVector.size(), &result);
1504     if (status != napi_ok) {
1505         HILOG_ERROR_I18N("Failed to create array");
1506         return nullptr;
1507     }
1508     FillInArrayElement(env, result, status, timeVector);
1509     return result;
1510 }
1511 
CompareString(napi_env env, napi_callback_info info)1512 napi_value IntlAddon::CompareString(napi_env env, napi_callback_info info)
1513 {
1514     size_t argc = 2;
1515     napi_value argv[2] = { 0 };
1516     napi_value thisVar = nullptr;
1517     void *data = nullptr;
1518     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1519 
1520     std::vector<char> first;
1521     if (!GetStringParameter(env, argv[0], first)) {
1522         return nullptr;
1523     }
1524 
1525     std::vector<char> second;
1526     if (!GetStringParameter(env, argv[1], second)) {
1527         return nullptr;
1528     }
1529 
1530     IntlAddon *obj = nullptr;
1531     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1532     if (status != napi_ok || !obj || !obj->collator_) {
1533         HILOG_ERROR_I18N("CompareString: Get Collator object failed");
1534         return nullptr;
1535     }
1536 
1537     CompareResult compareResult = obj->collator_->Compare(first.data(), second.data());
1538     napi_value result = nullptr;
1539     status = napi_create_int32(env, compareResult, &result);
1540     if (status != napi_ok) {
1541         HILOG_ERROR_I18N("Create compare result failed");
1542         return nullptr;
1543     }
1544 
1545     return result;
1546 }
1547 
GetCollatorResolvedOptions(napi_env env, napi_callback_info info)1548 napi_value IntlAddon::GetCollatorResolvedOptions(napi_env env, napi_callback_info info)
1549 {
1550     napi_value thisVar = nullptr;
1551     void *data = nullptr;
1552     napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
1553 
1554     IntlAddon *obj = nullptr;
1555     napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1556     if (status != napi_ok || !obj || !obj->collator_) {
1557         HILOG_ERROR_I18N("GetCollatorResolvedOptions: Get Collator object failed");
1558         return nullptr;
1559     }
1560     napi_value result = nullptr;
1561     napi_create_object(env, &result);
1562     std::map<std::string, std::string> options = {};
1563     obj->collator_->ResolvedOptions(options);
1564     SetOptionProperties(env, result, options, "localeMatcher");
1565     SetOptionProperties(env, result, options, "locale");
1566     SetOptionProperties(env, result, options, "usage");
1567     SetOptionProperties(env, result, options, "sensitivity");
1568     SetBooleanOptionProperties(env, result, options, "ignorePunctuation");
1569     SetBooleanOptionProperties(env, result, options, "numeric");
1570     SetOptionProperties(env, result, options, "caseFirst");
1571     SetOptionProperties(env, result, options, "collation");
1572     return result;
1573 }
1574 
GetPluralRulesType(napi_env env, napi_value options, std::map<std::string, std::string> &map)1575 void GetPluralRulesType(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1576 {
1577     GetOptionValue(env, options, "type", map);
1578     auto it = map.find("type");
1579     if (it != map.end()) {
1580         std::string type = it->second;
1581         if (type != "cardinal" && type != "ordinal") {
1582             HILOG_ERROR_I18N("invalid type");
1583             return;
1584         }
1585     } else {
1586         map.insert(std::make_pair("type", "cardinal"));
1587     }
1588 }
1589 
GetPluralRulesInteger(napi_env env, napi_value options, std::map<std::string, std::string> &map)1590 void GetPluralRulesInteger(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1591 {
1592     GetIntegerOptionValue(env, options, "minimumIntegerDigits", map);
1593     auto it = map.find("minimumIntegerDigits");
1594     if (it != map.end()) {
1595         std::string minimumIntegerDigits = it->second;
1596         int32_t status = 0;
1597         int n = ConvertString2Int(minimumIntegerDigits, status);
1598         if (status == -1 || n < 1 || n > 21) {  // the valid range of minimumIntegerDigits is [1, 21]
1599             HILOG_ERROR_I18N("invalid minimumIntegerDigits");
1600             return;
1601         }
1602     } else {
1603         map.insert(std::make_pair("minimumIntegerDigits", std::to_string(1)));
1604     }
1605 }
1606 
GetPluralRulesFractions(napi_env env, napi_value options, std::map<std::string, std::string> &map)1607 void GetPluralRulesFractions(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1608 {
1609     GetIntegerOptionValue(env, options, "minimumFractionDigits", map);
1610     auto it = map.find("minimumFractionDigits");
1611     if (it != map.end()) {
1612         std::string minimumFractionDigits = it->second;
1613         int32_t status = 0;
1614         int n = ConvertString2Int(minimumFractionDigits, status);
1615         if (status == -1 || n < 0 || n > 20) {  // the valid range of minimumFractionDigits is [0, 20]
1616             HILOG_ERROR_I18N("invalid minimumFractionDigits");
1617             return;
1618         }
1619     }
1620 
1621     GetIntegerOptionValue(env, options, "maximumFractionDigits", map);
1622     it = map.find("maximumFractionDigits");
1623     if (it != map.end()) {
1624         std::string maximumFractionDigits = it->second;
1625         int32_t status = 0;
1626         int n = ConvertString2Int(maximumFractionDigits, status);
1627         if (status == -1 || n < 0 || n > 20) {  // the valid range of maximumFractionDigits is [0, 20]
1628             HILOG_ERROR_I18N("invalid maximumFractionDigits");
1629             return;
1630         }
1631     }
1632 }
1633 
GetPluralRulesSignificant(napi_env env, napi_value options, std::map<std::string, std::string> &map)1634 void GetPluralRulesSignificant(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1635 {
1636     int minSignificant = -1;
1637     GetIntegerOptionValue(env, options, "minimumSignificantDigits", map);
1638     auto it = map.find("minimumSignificantDigits");
1639     if (it != map.end()) {
1640         std::string minSignificantStr = it->second;
1641         int32_t status = 0;
1642         int minSignificantInt = ConvertString2Int(minSignificantStr, status);
1643         // the valid range of minSignificantInt is [1, 21]
1644         if (status == -1 || minSignificantInt < 1 || minSignificantInt > 21) {
1645             HILOG_ERROR_I18N("invalid minimumSignificantDigits");
1646             return;
1647         }
1648         minSignificant = minSignificantInt;
1649     } else {
1650         minSignificant = 1;
1651     }
1652 
1653     GetIntegerOptionValue(env, options, "maximumSignificantDigits", map);
1654     it = map.find("maximumSignificantDigits");
1655     if (it != map.end()) {
1656         std::string maxSignificantStr = it->second;
1657         int32_t status = 0;
1658         int maxSignificant = ConvertString2Int(maxSignificantStr, status);
1659         // the valid range of minSignificant is [minSignificant, 21]
1660         if (status == -1 || maxSignificant < minSignificant || maxSignificant > 21) {
1661             HILOG_ERROR_I18N("invalid maximumSignificantDigits");
1662             return;
1663         }
1664     }
1665 }
1666 
GetPluralRulesOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)1667 void GetPluralRulesOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
1668 {
1669     GetCollatorLocaleMatcher(env, options, map);
1670     GetPluralRulesType(env, options, map);
1671     GetPluralRulesInteger(env, options, map);
1672     GetPluralRulesFractions(env, options, map);
1673     GetPluralRulesSignificant(env, options, map);
1674 }
1675 
InitPluralRules(napi_env env, napi_value exports)1676 napi_value IntlAddon::InitPluralRules(napi_env env, napi_value exports)
1677 {
1678     napi_status status = napi_ok;
1679     napi_property_descriptor properties[] = {
1680         DECLARE_NAPI_FUNCTION("select", Select)
1681     };
1682 
1683     napi_value constructor = nullptr;
1684     status = napi_define_class(env, "PluralRules", NAPI_AUTO_LENGTH, PluralRulesConstructor, nullptr,
1685         sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
1686     if (status != napi_ok) {
1687         HILOG_ERROR_I18N("Define class failed when InitPluralRules");
1688         return nullptr;
1689     }
1690 
1691     status = napi_set_named_property(env, exports, "PluralRules", constructor);
1692     if (status != napi_ok) {
1693         HILOG_ERROR_I18N("Set property failed when InitPluralRules");
1694         return nullptr;
1695     }
1696     return exports;
1697 }
1698 
PluralRulesConstructor(napi_env env, napi_callback_info info)1699 napi_value IntlAddon::PluralRulesConstructor(napi_env env, napi_callback_info info)
1700 {
1701     size_t argc = 2;
1702     napi_value argv[2] = { nullptr };
1703     napi_value thisVar = nullptr;
1704     void *data = nullptr;
1705     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1706     if (status != napi_ok) {
1707         return nullptr;
1708     }
1709     napi_valuetype valueType = napi_valuetype::napi_undefined;
1710     std::vector<std::string> localeTags;
1711     if (argc > 0) {
1712         napi_typeof(env, argv[0], &valueType);
1713         bool isArray = false;
1714         napi_is_array(env, argv[0], &isArray);
1715         if (valueType == napi_valuetype::napi_string) {
1716             GetLocaleTags(env, argv[0], localeTags);
1717         } else if (isArray) {
1718             uint32_t arrayLength = 0;
1719             napi_get_array_length(env, argv[0], &arrayLength);
1720             napi_value element = nullptr;
1721             for (uint32_t i = 0; i < arrayLength; i++) {
1722                 napi_get_element(env, argv[0], i, &element);
1723                 GetLocaleTags(env, element, localeTags);
1724             }
1725         }
1726     }
1727     std::map<std::string, std::string> map = {};
1728     if (argc > 1) {
1729         GetPluralRulesOptionValues(env, argv[1], map);
1730     }
1731     std::unique_ptr<IntlAddon> obj = nullptr;
1732     obj = std::make_unique<IntlAddon>();
1733     status =
1734         napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
1735     if (status != napi_ok) {
1736         HILOG_ERROR_I18N("PluralRulesConstructor: Wrap IntlAddon failed");
1737         return nullptr;
1738     }
1739     if (!obj->InitPluralRulesContext(env, info, localeTags, map)) {
1740         HILOG_ERROR_I18N("PluralRulesConstructor: Init DateTimeFormat failed");
1741         return nullptr;
1742     }
1743     obj.release();
1744     return thisVar;
1745 }
1746 
InitPluralRulesContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags, std::map<std::string, std::string> &map)1747 bool IntlAddon::InitPluralRulesContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
1748     std::map<std::string, std::string> &map)
1749 {
1750     napi_value global = nullptr;
1751     napi_status status = napi_get_global(env, &global);
1752     if (status != napi_ok) {
1753         HILOG_ERROR_I18N("InitPluralRulesContext: Get global failed");
1754         return false;
1755     }
1756     env_ = env;
1757     pluralrules_ = std::make_unique<PluralRules>(localeTags, map);
1758 
1759     return pluralrules_ != nullptr;
1760 }
1761 
Select(napi_env env, napi_callback_info info)1762 napi_value IntlAddon::Select(napi_env env, napi_callback_info info)
1763 {
1764     size_t argc = 1;
1765     napi_value argv[1] = { 0 };
1766     napi_value thisVar = nullptr;
1767     void *data = nullptr;
1768     napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1769     napi_valuetype valueType = napi_valuetype::napi_undefined;
1770     napi_typeof(env, argv[0], &valueType);
1771     if (valueType != napi_valuetype::napi_number) {
1772         HILOG_ERROR_I18N("Select: Parameter type does not match");
1773         return nullptr;
1774     }
1775 
1776     double number = 0;
1777     napi_status status = napi_get_value_double(env, argv[0], &number);
1778     if (status != napi_ok) {
1779         HILOG_ERROR_I18N("Select: Get number failed");
1780         return nullptr;
1781     }
1782 
1783     IntlAddon *obj = nullptr;
1784     status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1785     if (status != napi_ok || !obj || !obj->pluralrules_) {
1786         HILOG_ERROR_I18N("Get PluralRules object failed");
1787         return nullptr;
1788     }
1789 
1790     std::string res = obj->pluralrules_->Select(number);
1791     napi_value result = nullptr;
1792     status = napi_create_string_utf8(env, res.c_str(), NAPI_AUTO_LENGTH, &result);
1793     if (status != napi_ok) {
1794         HILOG_ERROR_I18N("get select result failed");
1795         return nullptr;
1796     }
1797     return result;
1798 }
1799 
Init(napi_env env, napi_value exports)1800 napi_value Init(napi_env env, napi_value exports)
1801 {
1802     napi_value val = IntlAddon::InitLocale(env, exports);
1803     val = IntlAddon::InitDateTimeFormat(env, val);
1804     val = IntlAddon::InitNumberFormat(env, val);
1805     val = IntlAddon::InitCollator(env, val);
1806     val = IntlAddon::InitRelativeTimeFormat(env, val);
1807     return IntlAddon::InitPluralRules(env, val);
1808 }
1809 
1810 static napi_module g_intlModule = {
1811     .nm_version = 1,
1812     .nm_flags = 0,
1813     .nm_filename = nullptr,
1814     .nm_register_func = Init,
1815     .nm_modname = "intl",
1816     .nm_priv = nullptr,
1817     .reserved = { 0 }
1818 };
1819 
AbilityRegister()1820 extern "C" __attribute__((constructor)) void AbilityRegister()
1821 {
1822     napi_module_register(&g_intlModule);
1823 }
1824 } // namespace I18n
1825 } // namespace Global
1826 } // namespace OHOS
1827