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