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