• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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