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