1 /*
2 * Copyright (c) 2024 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 "styled_number_format_addon.h"
17
18 #include "error_util.h"
19 #include "i18n_hilog.h"
20 #include "variable_convertor.h"
21
22 namespace OHOS {
23 namespace Global {
24 namespace I18n {
StyledNumberFormatAddon()25 StyledNumberFormatAddon::StyledNumberFormatAddon()
26 {
27 }
28
~StyledNumberFormatAddon()29 StyledNumberFormatAddon::~StyledNumberFormatAddon()
30 {
31 for (auto iter = styledNumberFormatOptions_.begin(); iter != styledNumberFormatOptions_.end(); ++iter) {
32 napi_delete_reference(env_, iter->second);
33 }
34 }
35
Destructor(napi_env env,void * nativeObject,void * hint)36 void StyledNumberFormatAddon::Destructor(napi_env env, void *nativeObject, void *hint)
37 {
38 if (!nativeObject) {
39 return;
40 }
41 delete reinterpret_cast<StyledNumberFormatAddon *>(nativeObject);
42 nativeObject = nullptr;
43 }
44
InitStyledNumberFormat(napi_env env,napi_value exports)45 napi_value StyledNumberFormatAddon::InitStyledNumberFormat(napi_env env, napi_value exports)
46 {
47 napi_property_descriptor properties[] = {
48 DECLARE_NAPI_FUNCTION("format", Format)
49 };
50 napi_value styledNumberFormatConstructor = nullptr;
51 napi_status status = napi_define_class(env, "StyledNumberFormat", NAPI_AUTO_LENGTH, constructor, nullptr,
52 sizeof(properties) / sizeof(napi_property_descriptor), properties, &styledNumberFormatConstructor);
53 if (status != napi_ok) {
54 HILOG_ERROR_I18N("Failed to define class StyledNumberFormat at Init.");
55 return nullptr;
56 }
57 status = napi_set_named_property(env, exports, "StyledNumberFormat", styledNumberFormatConstructor);
58 if (status != napi_ok) {
59 HILOG_ERROR_I18N("Set property failed when InitStyledNumberFormat");
60 return nullptr;
61 }
62 return exports;
63 }
64
constructor(napi_env env,napi_callback_info info)65 napi_value StyledNumberFormatAddon::constructor(napi_env env, napi_callback_info info)
66 {
67 size_t argc = 2;
68 napi_value argv[2] = { nullptr };
69 napi_value thisVar = nullptr;
70 void *data = nullptr;
71 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
72 if (status != napi_ok) {
73 HILOG_ERROR_I18N("StyledNumberFormatAddon::constructor get callback info error");
74 return nullptr;
75 }
76
77 if (argc < 1) {
78 ErrorUtil::NapiNotFoundError(env, I18N_NOT_FOUND, "numberFormat", true);
79 return nullptr;
80 }
81
82 NumberFormatAddon *numberFormatAddon = nullptr;
83 SimpleNumberFormatAddon *simpleNumberFormatAddon = nullptr;
84
85 GetNumberFmtOrSimpleNumberFmt(env, argv[0], &numberFormatAddon, &simpleNumberFormatAddon);
86
87 if (!numberFormatAddon && !simpleNumberFormatAddon) {
88 ErrorUtil::NapiThrow(env, I18N_NOT_FOUND, "numberFormat", "intl.NumberFormat or SimpleNumberFormat", true);
89 return nullptr;
90 }
91
92 std::unique_ptr<StyledNumberFormatAddon> obj = std::make_unique<StyledNumberFormatAddon>();
93 if (!obj) {
94 HILOG_ERROR_I18N("StyledNumberFormatAddon::constructor make unique ptr error");
95 return nullptr;
96 }
97 status = napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), Destructor, nullptr, nullptr);
98 if (status != napi_ok) {
99 HILOG_ERROR_I18N("StyledNumberFormatAddon::constructor napi_wrap error");
100 return nullptr;
101 }
102 if (!obj->InitStyledNumberFormatContent(env, numberFormatAddon, simpleNumberFormatAddon, argv[1])) {
103 return nullptr;
104 }
105 obj.release();
106 return thisVar;
107 }
108
Format(napi_env env,napi_callback_info info)109 napi_value StyledNumberFormatAddon::Format(napi_env env, napi_callback_info info)
110 {
111 size_t argc = 1;
112 napi_value argv[1] = { 0 };
113 napi_value thisVar = nullptr;
114 void *data = nullptr;
115 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
116 if (status != napi_ok) {
117 HILOG_ERROR_I18N("StyledNumberFormatAddon::Format: Get cb info failed.");
118 return VariableConvertor::CreateString(env, "");
119 } else if (argc < 1) {
120 ErrorUtil::NapiNotFoundError(env, I18N_NOT_FOUND, "value", true);
121 return VariableConvertor::CreateString(env, "");
122 }
123
124 napi_valuetype valueType = napi_valuetype::napi_undefined;
125 status = napi_typeof(env, argv[0], &valueType);
126 if (status != napi_ok) {
127 HILOG_ERROR_I18N("StyledNumberFormatAddon::Format: Failed to get type of argv[0].");
128 return VariableConvertor::CreateString(env, "");
129 } else if (valueType != napi_valuetype::napi_number) {
130 ErrorUtil::NapiThrow(env, I18N_NOT_FOUND, "value", "number", true);
131 return VariableConvertor::CreateString(env, "");
132 }
133
134 double number = 0;
135 napi_get_value_double(env, argv[0], &number);
136 StyledNumberFormatAddon *obj = nullptr;
137 status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
138 if (status != napi_ok || !obj || !obj->styledNumberFormat_) {
139 HILOG_ERROR_I18N("Format: Get StyledNumberFormat object failed");
140 return VariableConvertor::CreateString(env, "");
141 }
142
143 std::string formattedNumber = obj->styledNumberFormat_->Format(number);
144 std::vector<StyledNumberFormat::NumberPart> numberParts = obj->styledNumberFormat_->ParseToParts(number);
145
146 napi_value styleOption = CreateStyleOptions(env, numberParts, obj->styledNumberFormatOptions_);
147
148 napi_value styledString = CreateStyledString(env, formattedNumber, styleOption);
149 return styledString;
150 }
151
InitStyledNumberFormatContent(napi_env env,NumberFormatAddon * numberFormatAddon,SimpleNumberFormatAddon * simpleNumberFormatAddon,napi_value options)152 bool StyledNumberFormatAddon::InitStyledNumberFormatContent(napi_env env, NumberFormatAddon* numberFormatAddon,
153 SimpleNumberFormatAddon* simpleNumberFormatAddon, napi_value options)
154 {
155 if (numberFormatAddon) {
156 styledNumberFormat_ =
157 std::make_unique<StyledNumberFormat>(true, numberFormatAddon->CopyNumberFormat(), nullptr);
158 } else if (simpleNumberFormatAddon) {
159 styledNumberFormat_ =
160 std::make_unique<StyledNumberFormat>(false, nullptr, simpleNumberFormatAddon->CopySimpleNumberFormat());
161 }
162
163 if (options) {
164 SetTextStyle(env, options, "integer");
165 SetTextStyle(env, options, "decimal");
166 SetTextStyle(env, options, "fraction");
167 SetTextStyle(env, options, "unit");
168 }
169
170 env_ = env;
171 return styledNumberFormat_ != nullptr;
172 }
173
SetTextStyle(napi_env env,napi_value options,const std::string & optionName)174 void StyledNumberFormatAddon::SetTextStyle(napi_env env, napi_value options, const std::string &optionName)
175 {
176 napi_value optionValue = nullptr;
177 napi_valuetype type = napi_undefined;
178 napi_status status = napi_typeof(env, options, &type);
179 if (status != napi_ok && type != napi_object) {
180 HILOG_ERROR_I18N("setTextStyle: option is not an object");
181 return;
182 }
183 bool hasProperty = false;
184 status = napi_has_named_property(env, options, optionName.c_str(), &hasProperty);
185 if (status != napi_ok || !hasProperty) {
186 HILOG_ERROR_I18N("setTextStyle: Has not named %{public}s property", optionName.c_str());
187 return;
188 }
189 status = napi_get_named_property(env, options, optionName.c_str(), &optionValue);
190 if (status != napi_ok) {
191 HILOG_ERROR_I18N("setTextStyle: Get named property for %{public}s failed.", optionName.c_str());
192 return;
193 }
194
195 styledNumberFormatOptions_[optionName] = nullptr;
196 if (!optionValue) {
197 HILOG_ERROR_I18N("setTextStyle: %{public}s is nullptr.", optionName.c_str());
198 return;
199 }
200 status = napi_create_reference(env, optionValue, 1, &styledNumberFormatOptions_[optionName]);
201 if (status != napi_ok) {
202 HILOG_ERROR_I18N("setTextStyle: create reference for %{public}s failed.", optionName.c_str());
203 styledNumberFormatOptions_[optionName] = nullptr;
204 return;
205 }
206 }
207
CreateStyledString(napi_env env,const std::string & formattedNumber,napi_value styleOption)208 napi_value StyledNumberFormatAddon::CreateStyledString(napi_env env, const std::string &formattedNumber,
209 napi_value styleOption)
210 {
211 size_t argc = 2; // StyledString need 2 args
212 napi_value argv[2] = {nullptr};
213 argv[0] = VariableConvertor::CreateString(env, formattedNumber);
214 argv[1] = styleOption;
215
216 napi_value global = nullptr;
217 napi_status status = napi_get_global(env, &global);
218 if (status != napi_ok) {
219 HILOG_ERROR_I18N("CreateStyledString: get global object failed");
220 return nullptr;
221 }
222 napi_value constructor = nullptr;
223 status = napi_get_named_property(env, global, "StyledString", &constructor);
224 if (status != napi_ok) {
225 HILOG_ERROR_I18N("CreateStyledString: get StyledString failed");
226 return nullptr;
227 }
228
229 napi_value styleString = nullptr;
230 status = napi_new_instance(env, constructor, argc, argv, &styleString);
231 if (status != napi_ok) {
232 HILOG_ERROR_I18N("CreateStyledString: napi_new_instance failed");
233 return nullptr;
234 }
235 return styleString;
236 }
237
CreateStyleOptions(napi_env env,const std::vector<StyledNumberFormat::NumberPart> & numberParts,const std::unordered_map<std::string,napi_ref> & styledNumberFormatOptions)238 napi_value StyledNumberFormatAddon::CreateStyleOptions(napi_env env,
239 const std::vector<StyledNumberFormat::NumberPart> &numberParts,
240 const std::unordered_map<std::string, napi_ref> &styledNumberFormatOptions)
241 {
242 napi_value result = nullptr;
243 napi_status status = napi_create_array(env, &result);
244 if (status != napi_ok) {
245 HILOG_ERROR_I18N("CreateStyleOption: create array failed");
246 return nullptr;
247 }
248
249 unsigned int index = 0;
250 for (size_t i = 0; i < numberParts.size(); ++i) {
251 auto iter = styledNumberFormatOptions.find(numberParts[i].part_name);
252 if (iter == styledNumberFormatOptions.end()) {
253 continue;
254 }
255 napi_ref textStyleRef = iter->second;
256 napi_value option = CreateStyleOption(env, numberParts[i], textStyleRef);
257 status = napi_set_element(env, result, index, option);
258 if (status != napi_ok) {
259 HILOG_ERROR_I18N("CreateStyleoption: set array %{public}u failed", index);
260 return nullptr;
261 }
262 index += 1;
263 }
264 return result;
265 }
266
CreateStyleOption(napi_env env,StyledNumberFormat::NumberPart numberPart,napi_ref textStyleRef)267 napi_value StyledNumberFormatAddon::CreateStyleOption(napi_env env,
268 StyledNumberFormat::NumberPart numberPart, napi_ref textStyleRef)
269 {
270 if (!textStyleRef) {
271 HILOG_ERROR_I18N("CreateStyleoption: options do not have %{public}s",
272 numberPart.part_name.c_str());
273 return nullptr;
274 }
275 napi_value textStyle = nullptr;
276 napi_status status = napi_get_reference_value(env, textStyleRef, &textStyle);
277 if (status != napi_ok) {
278 HILOG_ERROR_I18N("CreateStyleoption: Failed to create reference at textStyle %{public}s",
279 numberPart.part_name.c_str());
280 return nullptr;
281 }
282 napi_value option = nullptr;
283 status = napi_create_object(env, &option);
284 if (status != napi_ok) {
285 HILOG_ERROR_I18N("CreateStyleoption: create object failed");
286 return nullptr;
287 }
288 napi_value start = nullptr;
289 status = napi_create_int32(env, numberPart.start, &start);
290 if (status != napi_ok) {
291 HILOG_ERROR_I18N("CreateStyleoption: create int32 failed");
292 return nullptr;
293 }
294 napi_value length = nullptr;
295 status = napi_create_int32(env, numberPart.length, &length);
296 if (status != napi_ok) {
297 HILOG_ERROR_I18N("CreateStyleoption: create int32 failed");
298 return nullptr;
299 }
300 napi_value styledKey = nullptr;
301 status = napi_create_int32(env, 0, &styledKey);
302 if (status != napi_ok) {
303 HILOG_ERROR_I18N("CreateStyleoption: create int32 failed");
304 return nullptr;
305 }
306 SetNamedProperty(env, option, "start", start);
307 SetNamedProperty(env, option, "length", length);
308 SetNamedProperty(env, option, "styledKey", styledKey);
309 SetNamedProperty(env, option, "styledValue", textStyle);
310 return option;
311 }
312
SetNamedProperty(napi_env env,napi_value & option,const std::string & name,napi_value & property)313 void StyledNumberFormatAddon::SetNamedProperty(napi_env env, napi_value &option, const std::string& name,
314 napi_value &property)
315 {
316 napi_status status = napi_set_named_property(env, option, name.c_str(), property);
317 if (status != napi_ok) {
318 HILOG_ERROR_I18N("CreateStyleoption: set property %{public}s failed", name.c_str());
319 }
320 }
321
GetNumberFmtOrSimpleNumberFmt(napi_env env,napi_value arg,NumberFormatAddon ** numberFormatAddon,SimpleNumberFormatAddon ** simpleNumberFormatAddon)322 void StyledNumberFormatAddon::GetNumberFmtOrSimpleNumberFmt(napi_env env, napi_value arg,
323 NumberFormatAddon **numberFormatAddon, SimpleNumberFormatAddon **simpleNumberFormatAddon)
324 {
325 napi_valuetype valueType = napi_valuetype::napi_undefined;
326 napi_status status = napi_typeof(env, arg, &valueType);
327 if (status != napi_ok) {
328 HILOG_ERROR_I18N("StyledNumberFormatAddon::constructor Failed to get type.");
329 return;
330 } else if (valueType != napi_valuetype::napi_object) {
331 ErrorUtil::NapiThrow(env, I18N_NOT_FOUND, "intl.NumberFormat or SimpleNumberFormat", "", true);
332 return;
333 }
334
335 napi_value numFmtConstructor = nullptr;
336 napi_value intlModule;
337 status = napi_load_module(env, "@ohos.intl", &intlModule);
338 if (status != napi_ok) {
339 HILOG_ERROR_I18N("StyledNumberFormatAddon::constructor Failed to get intl module.");
340 return;
341 }
342 status = napi_get_named_property(env, intlModule, "NumberFormat", &numFmtConstructor);
343 if (status != napi_ok) {
344 HILOG_ERROR_I18N("StyledNumberFormatAddon::constructo: get NumberFormat constructor failed");
345 return;
346 }
347
348 bool isNumberFormat = true;
349 status = napi_instanceof(env, arg, numFmtConstructor, &isNumberFormat);
350 if (status != napi_ok) {
351 HILOG_ERROR_I18N("StyledNumberFormatAddon::constructor Failed to get instance of argv 0.");
352 return;
353 }
354 if (isNumberFormat) {
355 status = napi_unwrap(env, arg, reinterpret_cast<void **>(numberFormatAddon));
356 if (status != napi_ok) {
357 *numberFormatAddon = nullptr;
358 }
359 } else {
360 status = napi_unwrap(env, arg, reinterpret_cast<void **>(simpleNumberFormatAddon));
361 if (status != napi_ok) {
362 *simpleNumberFormatAddon = nullptr;
363 }
364 }
365 }
366 } // namespace I18n
367 } // namespace Global
368 } // namespace OHOS