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