1 /**
2 * Copyright (c) 2024-2025 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 "plugins/ets/stdlib/native/core/stdlib_ani_helpers.h"
17 #include "IntlCommon.h"
18 #include "IntlState.h"
19 #include "IntlNumberFormatters.h"
20 #include "IntlNumberFormat.h"
21 #include <unicode/formattedvalue.h>
22 #include <unicode/numsys.h>
23 #include <algorithm>
24 #include <optional>
25 #include <cstring>
26 #include <cstdlib>
27 #include <array>
28 #include <utility>
29
30 namespace ark::ets::stdlib::intl {
31
32 constexpr std::string_view INTEGER_FIELD = "integer";
33 constexpr std::string_view FRACTION_FIELD = "fraction";
34 constexpr std::string_view DECIMAL_SEPARATOR_FIELD = "decimal";
35 constexpr std::string_view EXPONENT_SYMBOL_FIELD = "exponentSeparator";
36 constexpr std::string_view GROUPING_SEPARATOR_FIELD = "group";
37 constexpr std::string_view CURRENCY_FIELD = "currency";
38 constexpr std::string_view PERCENT_FIELD = "percentSign";
39 constexpr std::string_view PERMILL_FIELD = "perMille";
40 constexpr std::string_view PLUS_SIGN = "plusSign";
41 constexpr std::string_view MINUS_SIGN = "minusSign";
42 constexpr std::string_view EXPONENT_MINUS_SIGN = "exponentMinusSign";
43 constexpr std::string_view EXPONENT_INTEGER = "exponentInteger";
44 constexpr std::string_view COMPACT_FIELD = "compact";
45 constexpr std::string_view MEASURE_UNIT_FIELD = "unit";
46 constexpr std::string_view APPROXIMATELY_SIGN_FIELD = "approximatelySign";
47
48 /// Fallback
49 constexpr std::string_view LITERAL_FIELD = "literal";
50
51 /// Sources
52 constexpr std::string_view START_RANGE_SOURCE = "startRange";
53 constexpr std::string_view END_RANGE_SOURCE = "endRange";
54 constexpr std::string_view SHARED_SOURCE = "shared";
55
56 constexpr int32_t INTL_NUMBER_RANGE_START = 0;
57 constexpr int32_t INTL_NUMBER_RANGE_END = 1;
58
59 class NumberFormatPart {
60 public:
61 std::string type;
62 std::string value;
63 std::string source;
64 };
65
66 /*
67 * FieldSpan class, used in formatToParts/formatRangeToParts
68 *
69 * Example:
70 * ------------------------------------------------
71 * Consider the formatted string: "-5678.9"
72 0: "sign", "-"
73 1: "integer", "5"
74 2: "group", ","
75 3: "integer", "678"
76 4: "decimal", "."
77 5: "fraction", "9"
78 */
79 class FieldSpan {
80 public:
FieldSpan(int32_t fieldType,int32_t startIndex,int32_t endIndex)81 FieldSpan(int32_t fieldType, int32_t startIndex, int32_t endIndex)
82 : fieldType_(fieldType), startIndex_(startIndex), endIndex_(endIndex)
83 {
84 }
85
86 // Accessors
GetFieldType() const87 int32_t GetFieldType() const
88 {
89 return fieldType_;
90 }
GetStartIndex() const91 int32_t GetStartIndex() const
92 {
93 return startIndex_;
94 }
GetEndIndex() const95 int32_t GetEndIndex() const
96 {
97 return endIndex_;
98 }
GetStartEndPositionsTuple() const99 std::tuple<int32_t, int32_t> GetStartEndPositionsTuple() const
100 {
101 return std::make_tuple(startIndex_, endIndex_);
102 }
103
104 private:
105 /// Indicates the type of formatting (e.g., integer, fraction, etc.).
106 int32_t fieldType_;
107 /// The starting index (inclusive) of the span in the formatted string.
108 int32_t startIndex_;
109 /// The ending index (exclusive) of the span.
110 int32_t endIndex_;
111 };
112
113 class SpanFolder {
114 public:
SpanFolder(std::vector<FieldSpan> spans)115 explicit SpanFolder(std::vector<FieldSpan> spans) : spans_ {std::move(spans)}, currentActiveSpan_ {spans_.at(0)}
116 {
117 activeSpanIndexStack_.push_back(0);
118 }
Fold()119 std::vector<FieldSpan> Fold()
120 {
121 if (spans_.size() <= 1) {
122 return spans_;
123 }
124 std::sort(spans_.begin(), spans_.end(), [](const auto &a, const auto &b) {
125 const auto [aStart, aEnd] = a.GetStartEndPositionsTuple();
126 const auto [bStart, bEnd] = b.GetStartEndPositionsTuple();
127 if (aStart < bStart) {
128 return true;
129 }
130 if (aStart > bStart) {
131 return false;
132 }
133 if (aEnd < bEnd) {
134 return false;
135 }
136 if (aEnd > bEnd) {
137 return true;
138 }
139 return a.GetFieldType() < b.GetFieldType();
140 });
141 std::vector<FieldSpan> nonOverlappingSegments;
142 // Stack to track the indexes of spans that are currently active/overlapping
143 size_t nextSpanIndex = 1;
144 // Total length to process (end of the first span, which should cover the entire string)
145 const int32_t totalStringLength = spans_.at(0).GetEndIndex();
146 while (currentPosition_ < totalStringLength) {
147 // Determine where the next segment starts
148 const int32_t nextSegmentStartPosition =
149 (nextSpanIndex < spans_.size()) ? spans_.at(nextSpanIndex).GetStartIndex() : totalStringLength;
150 if (currentPosition_ < nextSegmentStartPosition) {
151 if (ProcessSpansUntilNextSegment(nextSegmentStartPosition, nonOverlappingSegments)) {
152 return nonOverlappingSegments;
153 }
154 }
155 if (nextSpanIndex < spans_.size()) {
156 activeSpanIndexStack_.push_back(nextSpanIndex++);
157 currentActiveSpan_ = spans_.at(activeSpanIndexStack_.back());
158 }
159 }
160 return nonOverlappingSegments;
161 }
162
163 private:
164 /// The spans to process
165 std::vector<FieldSpan> spans_;
166 /// The current active span
167 FieldSpan currentActiveSpan_;
168 /// The stack of active span indexes
169 std::vector<size_t> activeSpanIndexStack_;
170 /// The current position
171 int32_t currentPosition_ {0};
172
173 // Adds a segment to the result vector
AddSegment(std::vector<FieldSpan> & segments,int32_t fieldType,int32_t start,int32_t end)174 static void AddSegment(std::vector<FieldSpan> &segments, int32_t fieldType, int32_t start, int32_t end)
175 {
176 segments.emplace_back(fieldType, start, end);
177 }
178
ProcessSpansUntilNextSegment(int32_t nextSegmentStartPosition,std::vector<FieldSpan> & nonOverlappingSegments)179 bool ProcessSpansUntilNextSegment(int32_t nextSegmentStartPosition, std::vector<FieldSpan> &nonOverlappingSegments)
180 {
181 while (currentActiveSpan_.GetEndIndex() < nextSegmentStartPosition) {
182 if (currentPosition_ < currentActiveSpan_.GetEndIndex()) {
183 AddSegment(nonOverlappingSegments, currentActiveSpan_.GetFieldType(), currentPosition_,
184 currentActiveSpan_.GetEndIndex());
185
186 currentPosition_ = currentActiveSpan_.GetEndIndex();
187 }
188 activeSpanIndexStack_.pop_back();
189 if (activeSpanIndexStack_.empty()) {
190 return true;
191 }
192 currentActiveSpan_ = spans_.at(activeSpanIndexStack_.back());
193 }
194 if (currentPosition_ < nextSegmentStartPosition) {
195 AddSegment(nonOverlappingSegments, currentActiveSpan_.GetFieldType(), currentPosition_,
196 nextSegmentStartPosition);
197 currentPosition_ = nextSegmentStartPosition;
198 }
199 return false;
200 }
201 };
202
GetFieldTypeString(const FieldSpan & field,const icu::UnicodeString & text)203 std::string_view GetFieldTypeString(const FieldSpan &field, const icu::UnicodeString &text)
204 {
205 switch (static_cast<UNumberFormatFields>(field.GetFieldType())) {
206 case UNUM_INTEGER_FIELD:
207 return INTEGER_FIELD;
208 case UNUM_FRACTION_FIELD:
209 return FRACTION_FIELD;
210 case UNUM_DECIMAL_SEPARATOR_FIELD:
211 return DECIMAL_SEPARATOR_FIELD;
212 case UNUM_EXPONENT_SYMBOL_FIELD:
213 return EXPONENT_SYMBOL_FIELD;
214 case UNUM_EXPONENT_SIGN_FIELD:
215 return EXPONENT_MINUS_SIGN;
216 case UNUM_EXPONENT_FIELD:
217 return EXPONENT_INTEGER;
218 case UNUM_GROUPING_SEPARATOR_FIELD:
219 return GROUPING_SEPARATOR_FIELD;
220 case UNUM_CURRENCY_FIELD:
221 return CURRENCY_FIELD;
222 case UNUM_PERCENT_FIELD:
223 return PERCENT_FIELD;
224 case UNUM_SIGN_FIELD:
225 return (text.charAt(field.GetStartIndex()) == '+') ? PLUS_SIGN : MINUS_SIGN;
226 case UNUM_PERMILL_FIELD:
227 return PERMILL_FIELD;
228 case UNUM_COMPACT_FIELD:
229 return COMPACT_FIELD;
230 case UNUM_MEASURE_UNIT_FIELD:
231 return MEASURE_UNIT_FIELD;
232 case UNUM_APPROXIMATELY_SIGN_FIELD:
233 return APPROXIMATELY_SIGN_FIELD;
234 default:
235 UNREACHABLE();
236 }
237 }
238
NormalizeIfNaN(ani_double value)239 static ani_double NormalizeIfNaN(ani_double value)
240 {
241 return std::isnan(value) ? std::numeric_limits<ani_double>::quiet_NaN() : value;
242 }
243
IcuFormatDouble(ani_env * env,ani_object self,ani_double value)244 ani_string IcuFormatDouble(ani_env *env, ani_object self, ani_double value)
245 {
246 ParsedOptions options;
247 ParseOptions(env, self, options);
248
249 ani_status err;
250 LocNumFmt formatter = g_intlState->fmtsCache.NumFmtsCacheInvalidation(env, options, err);
251 if (err == ANI_OK) {
252 UErrorCode status = U_ZERO_ERROR;
253 auto fmtNumber = formatter.formatDouble(NormalizeIfNaN(value), status);
254 if (UNLIKELY(U_FAILURE(status))) {
255 std::string message = "Icu formatter format failed " + std::string(u_errorName(status));
256 ThrowNewError(env, ERR_CLS_RUNTIME_EXCEPTION, message.c_str(), CTOR_SIGNATURE_STR);
257 return nullptr;
258 }
259 icu::UnicodeString ustr = fmtNumber.toString(status);
260 return UnicodeToAniStr(env, ustr);
261 }
262 return nullptr;
263 }
264
IcuFormatDecStr(ani_env * env,ani_object self,ani_string value)265 ani_string IcuFormatDecStr(ani_env *env, ani_object self, ani_string value)
266 {
267 ParsedOptions options;
268 ParseOptions(env, self, options);
269
270 ani_status err;
271 LocNumFmt formatter = g_intlState->fmtsCache.NumFmtsCacheInvalidation(env, options, err);
272 if (err == ANI_OK) {
273 const std::string &valueString = ConvertFromAniString(env, value);
274 const icu::StringPiece sp {valueString.data(), static_cast<int32_t>(valueString.size())};
275 UErrorCode status = U_ZERO_ERROR;
276 const icu::number::FormattedNumber &fmtNumber = formatter.formatDecimal(sp, status);
277 if (UNLIKELY(U_FAILURE(status))) {
278 std::string message = "Icu formatter format failed " + std::string(u_errorName(status));
279 ThrowNewError(env, ERR_CLS_RUNTIME_EXCEPTION, message.c_str(), CTOR_SIGNATURE_STR);
280 return nullptr;
281 }
282 icu::UnicodeString ustr = fmtNumber.toString(status);
283 return UnicodeToAniStr(env, ustr);
284 }
285 return nullptr;
286 }
287
IcuFormatRange(ani_env * env,ani_object self,const icu::Formattable & startFrmtbl,const icu::Formattable & endFrmtbl)288 ani_string IcuFormatRange(ani_env *env, ani_object self, const icu::Formattable &startFrmtbl,
289 const icu::Formattable &endFrmtbl)
290 {
291 ParsedOptions options;
292 ParseOptions(env, self, options);
293
294 ani_status err;
295 LocNumRangeFmt formatter = g_intlState->fmtsCache.NumRangeFmtsCacheInvalidation(env, options, err);
296 if (err == ANI_OK) {
297 UErrorCode status = U_ZERO_ERROR;
298 const icu::number::FormattedNumberRange &fmtRangeNumber =
299 formatter.formatFormattableRange(startFrmtbl, endFrmtbl, status);
300 if (UNLIKELY(U_FAILURE(status))) {
301 std::string message = "Icu range formatter format failed " + std::string(u_errorName(status));
302 ThrowNewError(env, ERR_CLS_RUNTIME_EXCEPTION, message.c_str(), CTOR_SIGNATURE_STR);
303 return nullptr;
304 }
305 icu::UnicodeString ustr = fmtRangeNumber.toString(status);
306 return UnicodeToAniStr(env, ustr);
307 }
308 return nullptr;
309 }
310
DoubleToFormattable(ani_env * env,double value)311 icu::Formattable DoubleToFormattable([[maybe_unused]] ani_env *env, double value)
312 {
313 return icu::Formattable(value);
314 }
315
StrToFormattable(ani_env * env,ani_string value)316 icu::Formattable StrToFormattable(ani_env *env, ani_string value)
317 {
318 UErrorCode status = U_ZERO_ERROR;
319 const std::string &str = ConvertFromAniString(env, value);
320 const icu::StringPiece sp {str.data(), static_cast<int32_t>(str.size())};
321 icu::Formattable ret(sp, status);
322 if (UNLIKELY(U_FAILURE(status))) {
323 std::string message = "StrToToFormattable failed " + std::string(u_errorName(status));
324 ThrowNewError(env, ERR_CLS_RUNTIME_EXCEPTION, message.c_str(), CTOR_SIGNATURE_STR);
325 return icu::Formattable();
326 }
327 return ret;
328 }
329
330 template <typename T>
331 struct GetICUFormattedNumber {
332 static std::optional<icu::number::FormattedNumber> GetFormattedNumber(
333 const icu::number::LocalizedNumberFormatter &formatter, T value);
334 };
335
336 template <>
337 struct GetICUFormattedNumber<ani_double> {
GetFormattedNumberark::ets::stdlib::intl::GetICUFormattedNumber338 static std::optional<icu::number::FormattedNumber> GetFormattedNumber(
339 const icu::number::LocalizedNumberFormatter &formatter, ani_double value)
340 {
341 UErrorCode status = U_ZERO_ERROR;
342 auto formatted = formatter.formatDouble(value, status);
343 if (U_FAILURE(status) != 0) {
344 return std::nullopt;
345 }
346 return formatted;
347 }
348 };
349
350 template <>
351 struct GetICUFormattedNumber<std::string> {
GetFormattedNumberark::ets::stdlib::intl::GetICUFormattedNumber352 static std::optional<icu::number::FormattedNumber> GetFormattedNumber(
353 const icu::number::LocalizedNumberFormatter &formatter, const std::string &value)
354 {
355 UErrorCode status = U_ZERO_ERROR;
356 const icu::StringPiece sp {value.data(), static_cast<int32_t>(value.size())};
357 auto formatted = formatter.formatDecimal(sp, status);
358 if (U_FAILURE(status) != 0) {
359 return std::nullopt;
360 }
361 return formatted;
362 }
363 };
364
ProcessSpansToFormatParts(const std::vector<FieldSpan> & flatParts,const icu::UnicodeString & formattedString,const std::pair<int32_t,int32_t> & startRangeSource,const std::pair<int32_t,int32_t> & endRangeSource,bool calculateSources)365 std::vector<NumberFormatPart> ProcessSpansToFormatParts(const std::vector<FieldSpan> &flatParts,
366 const icu::UnicodeString &formattedString,
367 const std::pair<int32_t, int32_t> &startRangeSource,
368 const std::pair<int32_t, int32_t> &endRangeSource,
369 bool calculateSources)
370 {
371 std::vector<NumberFormatPart> parts;
372 auto contains = [](std::pair<int32_t, int32_t> gold, std::pair<int32_t, int32_t> request) {
373 return gold.first <= request.first && gold.second >= request.second;
374 };
375
376 for (const auto &part : flatParts) {
377 std::string_view ty = LITERAL_FIELD;
378 std::string_view source = SHARED_SOURCE;
379
380 icu::UnicodeString sub;
381 formattedString.extractBetween(part.GetStartIndex(), part.GetEndIndex(), sub);
382 std::string value;
383 sub.toUTF8String(value);
384
385 if (part.GetFieldType() != -1) {
386 ty = GetFieldTypeString(part, formattedString);
387 }
388
389 if (calculateSources) {
390 auto request = std::make_pair(part.GetStartIndex(), part.GetEndIndex());
391 if (contains(startRangeSource, request)) {
392 source = START_RANGE_SOURCE;
393 } else if (contains(endRangeSource, request)) {
394 source = END_RANGE_SOURCE;
395 }
396 }
397 parts.push_back({std::string(ty), value, std::string(source)});
398 }
399 return parts;
400 }
401
ExtractParts(ani_env * env,const icu::FormattedValue & formatted,bool calculateSources)402 std::vector<NumberFormatPart> ExtractParts(ani_env *env, const icu::FormattedValue &formatted, bool calculateSources)
403 {
404 UErrorCode status = U_ZERO_ERROR;
405 std::vector<NumberFormatPart> parts;
406 icu::UnicodeString formattedString = formatted.toString(status);
407
408 if (UNLIKELY(U_FAILURE(status) != 0)) {
409 std::string message = "ExtractParts. Unable to convert formatted value to string";
410 ThrowNewError(env, "Lstd/core/RuntimeException;", message.c_str(), "Lstd/core/String;:V");
411 return parts;
412 }
413
414 std::pair<int32_t, int32_t> startRangeSource = std::make_pair(0, 0);
415 std::pair<int32_t, int32_t> endRangeSource = std::make_pair(0, 0);
416
417 std::vector<FieldSpan> spans;
418
419 // Add a default span for the entire string
420 spans.emplace_back(-1, 0, formattedString.length());
421 // Source (sep like "-", "~") for range to parts formatting looks like
422 // span is a representation of semi-open interval [start, end)
423 // For example "start: 0.123, end: 0.124"
424 // formatted: "-0.123-0.124"
425 // - 0 . 1 2 3 - 0 . 1 2 4
426 // |<--------->|
427 // |-|
428 // |<--------->|
429 // ^^^^^^^^^^^
430 // startRange <------------------ [0, 6)
431 // ^
432 // shared <------------------ [6, 7)
433 //
434 // ^^^^^^^^^^^^
435 // endRange <------------------ [7, 12)
436 icu::ConstrainedFieldPosition cfpos;
437 while (formatted.nextPosition(cfpos, status) != 0) {
438 if (UNLIKELY(U_FAILURE(status) != 0)) {
439 std::string message = "ExtractParts. Error during field iteration";
440 ThrowNewError(env, "Lstd/core/RuntimeException;", message.c_str(), "Lstd/core/String;:V");
441 return parts;
442 }
443 int32_t cat = cfpos.getCategory();
444 int32_t field = cfpos.getField();
445 int32_t start = cfpos.getStart();
446 int32_t limit = cfpos.getLimit();
447 if (cat == UFIELD_CATEGORY_NUMBER_RANGE_SPAN) {
448 switch (field) {
449 case INTL_NUMBER_RANGE_START:
450 startRangeSource.first = start;
451 startRangeSource.second = limit;
452 break;
453 case INTL_NUMBER_RANGE_END:
454 endRangeSource.first = start;
455 endRangeSource.second = limit;
456 break;
457 default:
458 UNREACHABLE();
459 }
460 } else {
461 spans.emplace_back(field, start, limit);
462 }
463 }
464 return ProcessSpansToFormatParts(SpanFolder(std::move(spans)).Fold(), formattedString, startRangeSource,
465 endRangeSource, calculateSources);
466 }
467
GetNumberFormatPart(ani_env * env,ani_string ty,ani_string value)468 ani_object GetNumberFormatPart(ani_env *env, ani_string ty, ani_string value)
469 {
470 ani_class numberFormatPartClass;
471 ANI_FATAL_IF_ERROR(env->FindClass("Lstd/core/Intl/NumberFormatPart;", &numberFormatPartClass));
472
473 ani_method constructorMethod;
474 ANI_FATAL_IF_ERROR(env->Class_FindMethod(numberFormatPartClass, "<ctor>", ":V", &constructorMethod));
475
476 ani_object numberFormatPartObj;
477
478 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
479 ANI_FATAL_IF_ERROR(env->Object_New(numberFormatPartClass, constructorMethod, &numberFormatPartObj));
480
481 ani_field typeField;
482 ANI_FATAL_IF_ERROR(env->Class_FindField(numberFormatPartClass, "type", &typeField));
483
484 ani_field valueField;
485 ANI_FATAL_IF_ERROR(env->Class_FindField(numberFormatPartClass, "value", &valueField));
486
487 ANI_FATAL_IF_ERROR(env->Object_SetField_Ref(numberFormatPartObj, typeField, ty));
488 ANI_FATAL_IF_ERROR(env->Object_SetField_Ref(numberFormatPartObj, valueField, value));
489
490 return numberFormatPartObj;
491 }
492
GetNumberFormatRangePart(ani_env * env,ani_string ty,ani_string value,ani_string source)493 ani_object GetNumberFormatRangePart(ani_env *env, ani_string ty, ani_string value, ani_string source)
494 {
495 ani_class numberRangeFormatPartClass;
496 ANI_FATAL_IF_ERROR(env->FindClass("Lstd/core/Intl/NumberRangeFormatPart;", &numberRangeFormatPartClass));
497
498 ani_method constructorMethod;
499 ANI_FATAL_IF_ERROR(env->Class_FindMethod(numberRangeFormatPartClass, "<ctor>", ":V", &constructorMethod));
500
501 ani_object numberRangeFormatPartObj;
502
503 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
504 ANI_FATAL_IF_ERROR(env->Object_New(numberRangeFormatPartClass, constructorMethod, &numberRangeFormatPartObj));
505
506 ani_field typeField;
507 ANI_FATAL_IF_ERROR(env->Class_FindField(numberRangeFormatPartClass, "type", &typeField));
508
509 ani_field valueField;
510 ANI_FATAL_IF_ERROR(env->Class_FindField(numberRangeFormatPartClass, "value", &valueField));
511
512 ani_field sourceField;
513 ANI_FATAL_IF_ERROR(env->Class_FindField(numberRangeFormatPartClass, "source", &sourceField));
514
515 ANI_FATAL_IF_ERROR(env->Object_SetField_Ref(numberRangeFormatPartObj, typeField, ty));
516 ANI_FATAL_IF_ERROR(env->Object_SetField_Ref(numberRangeFormatPartObj, valueField, value));
517 ANI_FATAL_IF_ERROR(env->Object_SetField_Ref(numberRangeFormatPartObj, sourceField, source));
518 return numberRangeFormatPartObj;
519 }
520
521 template <typename T>
IcuFormatToParts(ani_env * env,ani_object self,T value)522 ani_array_ref IcuFormatToParts(ani_env *env, [[maybe_unused]] ani_object self, [[maybe_unused]] T value)
523 {
524 ani_array_ref resultArray;
525 ParsedOptions options;
526 ParseOptions(env, self, options);
527
528 ani_status err;
529 LocNumFmt formatter = g_intlState->fmtsCache.NumFmtsCacheInvalidation(env, options, err);
530 if (err != ANI_OK) {
531 return nullptr;
532 }
533
534 auto fmtNumber = GetICUFormattedNumber<T>::GetFormattedNumber(formatter, value);
535 if (!fmtNumber) {
536 return nullptr;
537 }
538
539 std::vector<ani_object> resultParts;
540 UErrorCode status = U_ZERO_ERROR;
541 icu::UnicodeString ustr = fmtNumber->toString(status);
542
543 auto parts = ExtractParts(env, static_cast<const icu::FormattedValue &>(*fmtNumber), false);
544 for (const auto &part : parts) {
545 auto utype = CreateUtf8String(env, part.type.c_str(), part.type.size());
546 auto uvalue = CreateUtf8String(env, part.value.c_str(), part.value.size());
547 auto partObj = GetNumberFormatPart(env, utype, uvalue);
548 resultParts.push_back(partObj);
549 }
550
551 ani_class numberFormatPartClass;
552 ANI_FATAL_IF_ERROR(env->FindClass("Lstd/core/Intl/NumberFormatPart;", &numberFormatPartClass));
553 ani_ref undefined;
554 ANI_FATAL_IF_ERROR(env->GetUndefined(&undefined));
555 ANI_FATAL_IF_ERROR(env->Array_New_Ref(numberFormatPartClass, resultParts.size(), undefined, &resultArray));
556
557 for (size_t i = 0; i < resultParts.size(); ++i) {
558 ANI_FATAL_IF_ERROR(env->Array_Set_Ref(resultArray, i, resultParts[i]));
559 }
560
561 return resultArray;
562 }
563
IcuFormatToRangeParts(ani_env * env,ani_object self,const icu::Formattable & startFrmtbl,const icu::Formattable & endFrmtbl)564 ani_array_ref IcuFormatToRangeParts(ani_env *env, [[maybe_unused]] ani_object self,
565 [[maybe_unused]] const icu::Formattable &startFrmtbl,
566 [[maybe_unused]] const icu::Formattable &endFrmtbl)
567 {
568 ani_array_ref resultArray;
569
570 ParsedOptions options;
571 ParseOptions(env, self, options);
572
573 ani_status err;
574 LocNumRangeFmt formatter = g_intlState->fmtsCache.NumRangeFmtsCacheInvalidation(env, options, err);
575 if (err != ANI_OK) {
576 return nullptr;
577 }
578
579 UErrorCode status = U_ZERO_ERROR;
580 icu::number::FormattedNumberRange formatted = formatter.formatFormattableRange(startFrmtbl, endFrmtbl, status);
581
582 if (U_FAILURE(status) != 0) {
583 return nullptr;
584 }
585
586 std::vector<NumberFormatPart> parts = ExtractParts(env, formatted, true);
587
588 ani_class numberRangeFormatPartClass;
589 ANI_FATAL_IF_ERROR(env->FindClass("Lstd/core/Intl/NumberRangeFormatPart;", &numberRangeFormatPartClass));
590 ani_ref undefined;
591 ANI_FATAL_IF_ERROR(env->GetUndefined(&undefined));
592 ANI_FATAL_IF_ERROR(env->Array_New_Ref(numberRangeFormatPartClass, parts.size(), undefined, &resultArray));
593
594 for (size_t i = 0; i < parts.size(); ++i) {
595 auto typeString = CreateUtf8String(env, parts[i].type.c_str(), parts[i].type.size());
596 auto valueString = CreateUtf8String(env, parts[i].value.c_str(), parts[i].value.size());
597 auto sourceString = CreateUtf8String(env, parts[i].source.c_str(), parts[i].source.size());
598 auto part = GetNumberFormatRangePart(env, typeString, valueString, sourceString);
599 ANI_FATAL_IF_ERROR(env->Array_Set_Ref(resultArray, i, part));
600 }
601
602 return resultArray;
603 }
604
IcuFormatRangeDoubleDouble(ani_env * env,ani_object self,ani_double startValue,ani_double endValue)605 ani_string IcuFormatRangeDoubleDouble(ani_env *env, ani_object self, ani_double startValue, ani_double endValue)
606 {
607 return IcuFormatRange(env, self, DoubleToFormattable(env, startValue), DoubleToFormattable(env, endValue));
608 }
609
IcuFormatRangeDoubleDecStr(ani_env * env,ani_object self,ani_double startValue,ani_string endValue)610 ani_string IcuFormatRangeDoubleDecStr(ani_env *env, ani_object self, ani_double startValue, ani_string endValue)
611 {
612 return IcuFormatRange(env, self, DoubleToFormattable(env, startValue), StrToFormattable(env, endValue));
613 }
614
IcuFormatRangeDecStrDouble(ani_env * env,ani_object self,ani_string startValue,ani_double endValue)615 ani_string IcuFormatRangeDecStrDouble(ani_env *env, ani_object self, ani_string startValue, ani_double endValue)
616 {
617 return IcuFormatRange(env, self, StrToFormattable(env, startValue), DoubleToFormattable(env, endValue));
618 }
619
IcuFormatRangeDecStrDecStr(ani_env * env,ani_object self,ani_string startValue,ani_string endValue)620 ani_string IcuFormatRangeDecStrDecStr(ani_env *env, ani_object self, ani_string startValue, ani_string endValue)
621 {
622 return IcuFormatRange(env, self, StrToFormattable(env, startValue), StrToFormattable(env, endValue));
623 }
624
IcuFormatToPartsDouble(ani_env * env,ani_object self,ani_double value)625 ani_array_ref IcuFormatToPartsDouble(ani_env *env, [[maybe_unused]] ani_object self, [[maybe_unused]] ani_double value)
626 {
627 return IcuFormatToParts<ani_double>(env, self, value);
628 }
629
IcuFormatToPartsDecStr(ani_env * env,ani_object self,ani_string value)630 ani_array_ref IcuFormatToPartsDecStr(ani_env *env, [[maybe_unused]] ani_object self, [[maybe_unused]] ani_string value)
631 {
632 return IcuFormatToParts<std::string>(env, self, ConvertFromAniString(env, value));
633 }
634
IcuFormatToRangePartsDoubleDouble(ani_env * env,ani_object self,ani_double startValue,ani_double endValue)635 ani_array_ref IcuFormatToRangePartsDoubleDouble(ani_env *env, [[maybe_unused]] ani_object self,
636 [[maybe_unused]] ani_double startValue,
637 [[maybe_unused]] ani_double endValue)
638 {
639 return IcuFormatToRangeParts(env, self, DoubleToFormattable(env, startValue), DoubleToFormattable(env, endValue));
640 }
641
IcuFormatToRangePartsDoubleDecStr(ani_env * env,ani_object self,ani_double startValue,ani_string endValue)642 ani_array_ref IcuFormatToRangePartsDoubleDecStr(ani_env *env, [[maybe_unused]] ani_object self,
643 [[maybe_unused]] ani_double startValue,
644 [[maybe_unused]] ani_string endValue)
645 {
646 return IcuFormatToRangeParts(env, self, DoubleToFormattable(env, startValue), StrToFormattable(env, endValue));
647 }
648
IcuFormatToRangePartsDecStrDouble(ani_env * env,ani_object self,ani_string startValue,ani_double endValue)649 ani_array_ref IcuFormatToRangePartsDecStrDouble(ani_env *env, [[maybe_unused]] ani_object self,
650 [[maybe_unused]] ani_string startValue,
651 [[maybe_unused]] ani_double endValue)
652 {
653 return IcuFormatToRangeParts(env, self, StrToFormattable(env, startValue), DoubleToFormattable(env, endValue));
654 }
655
IcuFormatToRangePartsDecStrDecStr(ani_env * env,ani_object self,ani_string startValue,ani_string endValue)656 ani_array_ref IcuFormatToRangePartsDecStrDecStr(ani_env *env, [[maybe_unused]] ani_object self,
657 [[maybe_unused]] ani_string startValue,
658 [[maybe_unused]] ani_string endValue)
659 {
660 return IcuFormatToRangeParts(env, self, StrToFormattable(env, startValue), StrToFormattable(env, endValue));
661 }
662
IsIcuUnitCorrect(ani_env * env,ani_object self,ani_string unit)663 ani_boolean IsIcuUnitCorrect(ani_env *env, [[maybe_unused]] ani_object self, ani_string unit)
664 {
665 return IsCorrectUnitIdentifier(ConvertFromAniString(env, unit)) ? ANI_TRUE : ANI_FALSE;
666 }
667
IcuNumberingSystem(ani_env * env,ani_object self,ani_string locale)668 ani_string IcuNumberingSystem(ani_env *env, [[maybe_unused]] ani_object self, ani_string locale)
669 {
670 icu::Locale icuLocale(icu::Locale::getDefault());
671 ani_status err = LocTagToIcuLocale(env, ConvertFromAniString(env, locale), icuLocale);
672 if (err == ANI_OK) {
673 UErrorCode status = U_ZERO_ERROR;
674 std::unique_ptr<icu::NumberingSystem> numSystem(icu::NumberingSystem::createInstance(icuLocale, status));
675 if (UNLIKELY(U_FAILURE(status))) {
676 std::string message = "NumberingSystem creation failed";
677 ThrowNewError(env, ERR_CLS_RUNTIME_EXCEPTION, message.c_str(), CTOR_SIGNATURE_STR);
678 return nullptr;
679 }
680 return CreateUtf8String(env, numSystem->getName(), strlen(numSystem->getName()));
681 }
682 return nullptr;
683 }
684
IcuCurrencyDigits(ani_env * env,ani_object self,ani_string currency)685 ani_double IcuCurrencyDigits(ani_env *env, [[maybe_unused]] ani_object self, ani_string currency)
686 {
687 UErrorCode status = U_ZERO_ERROR;
688 const std::string currencyStr = ConvertFromAniString(env, currency);
689 icu::UnicodeString uCurrency = icu::UnicodeString::fromUTF8(currencyStr);
690 int32_t fractionDigits = ucurr_getDefaultFractionDigits(uCurrency.getBuffer(), &status);
691 if (U_SUCCESS(status) != 0) {
692 return static_cast<double>(fractionDigits);
693 }
694 // return default fraction as 2 if ucurr_getDefaultFractionDigits failed
695 return 2;
696 }
697
RegisterIntlNumberFormatNativeMethods(ani_env * env)698 ani_status RegisterIntlNumberFormatNativeMethods(ani_env *env)
699 {
700 ani_class numberFormatClass;
701 ANI_FATAL_IF_ERROR(env->FindClass("Lstd/core/Intl/NumberFormat;", &numberFormatClass));
702
703 const auto methods = std::array {
704 ani_native_function {"getNumberingSystem", "Lstd/core/String;:Lstd/core/String;",
705 reinterpret_cast<void *>(IcuNumberingSystem)},
706 ani_native_function {"formatDouble", "D:Lstd/core/String;", reinterpret_cast<void *>(IcuFormatDouble)},
707 ani_native_function {"formatDecStr", "Lstd/core/String;:Lstd/core/String;",
708 reinterpret_cast<void *>(IcuFormatDecStr)},
709 ani_native_function {"formatRangeDoubleDouble", "DD:Lstd/core/String;",
710 reinterpret_cast<void *>(IcuFormatRangeDoubleDouble)},
711 ani_native_function {"formatRangeDoubleDecStr", "DLstd/core/String;:Lstd/core/String;",
712 reinterpret_cast<void *>(IcuFormatRangeDoubleDecStr)},
713 ani_native_function {"formatRangeDecStrDouble", "Lstd/core/String;D:Lstd/core/String;",
714 reinterpret_cast<void *>(IcuFormatRangeDecStrDouble)},
715 ani_native_function {"formatRangeDecStrDecStr", "Lstd/core/String;Lstd/core/String;:Lstd/core/String;",
716 reinterpret_cast<void *>(IcuFormatRangeDecStrDecStr)},
717 ani_native_function {"formatToPartsDouble", "D:[Lstd/core/Intl/NumberFormatPart;",
718 reinterpret_cast<void *>(IcuFormatToPartsDouble)},
719 ani_native_function {"formatToPartsDecStr", "Lstd/core/String;:[Lstd/core/Intl/NumberFormatPart;",
720 reinterpret_cast<void *>(IcuFormatToPartsDecStr)},
721 ani_native_function {"formatToRangePartsDoubleDouble", "DD:[Lstd/core/Intl/NumberRangeFormatPart;",
722 reinterpret_cast<void *>(IcuFormatToRangePartsDoubleDouble)},
723 ani_native_function {"formatToRangePartsDoubleDecStr",
724 "DLstd/core/String;:[Lstd/core/Intl/NumberRangeFormatPart;",
725 reinterpret_cast<void *>(IcuFormatToRangePartsDoubleDecStr)},
726 ani_native_function {"formatToRangePartsDecStrDouble",
727 "Lstd/core/String;D:[Lstd/core/Intl/NumberRangeFormatPart;",
728 reinterpret_cast<void *>(IcuFormatToRangePartsDecStrDouble)},
729 ani_native_function {"formatToRangePartsDecStrDecStr",
730 "Lstd/core/String;Lstd/core/String;:[Lstd/core/Intl/NumberRangeFormatPart;",
731 reinterpret_cast<void *>(IcuFormatToRangePartsDecStrDecStr)},
732 ani_native_function {"isUnitCorrect", "Lstd/core/String;:Z", reinterpret_cast<void *>(IsIcuUnitCorrect)},
733 ani_native_function {"currencyDigits", "Lstd/core/String;:D", reinterpret_cast<void *>(IcuCurrencyDigits)}};
734 ani_status status = env->Class_BindNativeMethods(numberFormatClass, methods.data(), methods.size());
735 if (!(status == ANI_OK || status == ANI_ALREADY_BINDED)) {
736 return status;
737 }
738 return ANI_OK;
739 }
740
741 } // namespace ark::ets::stdlib::intl
742