1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 #ifndef intl_components_ListFormat_h_
5 #define intl_components_ListFormat_h_
7 #include "mozilla/CheckedInt.h"
8 #include "mozilla/intl/ICU4CGlue.h"
9 #include "mozilla/PodOperations.h"
10 #include "mozilla/Result.h"
11 #include "mozilla/Vector.h"
12 #include "unicode/ulistformatter.h"
14 struct UListFormatter
;
16 namespace mozilla::intl
{
18 static constexpr size_t DEFAULT_LIST_LENGTH
= 8;
21 * This component is a Mozilla-focused API for the list formatting provided by
22 * ICU. It implements the API provided by the ECMA-402 Intl.ListFormat object.
24 * https://tc39.es/ecma402/#listformat-objects
26 class ListFormat final
{
29 * The [[Type]] and [[Style]] properties of ListFormat instances.
31 * https://tc39.es/ecma402/#sec-properties-of-intl-listformat-instances
34 enum class Type
{ Conjunction
, Disjunction
, Unit
};
36 enum class Style
{ Long
, Short
, Narrow
};
39 * The 'options' object to create Intl.ListFormat instance.
41 * https://tc39.es/ecma402/#sec-Intl.ListFormat
44 // "conjunction" is the default fallback value.
45 Type mType
= Type::Conjunction
;
47 // "long" is the default fallback value.
48 Style mStyle
= Style::Long
;
52 * Create a ListFormat object for the provided locale and options.
54 * https://tc39.es/ecma402/#sec-Intl.ListFormat
56 static Result
<UniquePtr
<ListFormat
>, ICUError
> TryCreate(
57 mozilla::Span
<const char> aLocale
, const Options
& aOptions
);
62 * The list of String values for FormatList and FormatListToParts.
64 * https://tc39.es/ecma402/#sec-formatlist
65 * https://tc39.es/ecma402/#sec-formatlisttoparts
68 mozilla::Vector
<mozilla::Span
<const char16_t
>, DEFAULT_LIST_LENGTH
>;
71 * Format the list according and write the result in buffer.
73 * https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.format
74 * https://tc39.es/ecma402/#sec-formatlist
76 template <typename Buffer
>
77 ICUResult
Format(const StringList
& list
, Buffer
& buffer
) const {
78 static_assert(std::is_same_v
<typename
Buffer::CharType
, char16_t
>,
79 "Currently only UTF-16 buffers are supported.");
81 mozilla::Vector
<const char16_t
*, DEFAULT_LIST_LENGTH
> u16strings
;
82 mozilla::Vector
<int32_t, DEFAULT_LIST_LENGTH
> u16stringLens
;
83 MOZ_TRY(ConvertStringListToVectors(list
, u16strings
, u16stringLens
));
85 int32_t u16stringCount
= mozilla::AssertedCast
<int32_t>(list
.length());
86 MOZ_TRY(FillBufferWithICUCall(
87 buffer
, [this, &u16strings
, &u16stringLens
, u16stringCount
](
88 char16_t
* chars
, int32_t size
, UErrorCode
* status
) {
89 return ulistfmt_format(mListFormatter
.GetConst(), u16strings
.begin(),
90 u16stringLens
.begin(), u16stringCount
, chars
,
98 * The corresponding list of parts according to the effective locale and the
99 * formatting options of ListFormat.
100 * Each part has a [[Type]] field, which must be "element" or "literal", and a
103 * To store Part more efficiently, it doesn't store the ||Value|| of type
104 * string in this struct. Instead, it stores the end index of the string in
105 * the buffer(which is passed to ListFormat::FormatToParts()). The begin index
106 * of the ||Value|| is the index of the previous part.
110 * +---------------+---------------+---------------+
111 * | Part[0].Value | Part[1].Value | Part[2].Value | ....
112 * +---------------+---------------+---------------+
114 * Part[0].index is i. Part[0].Value is stored in the Buffer[0..i].
115 * Part[1].index is j. Part[1].Value is stored in the Buffer[i..j].
117 * See https://tc39.es/ecma402/#sec-createpartsfromlist
119 enum class PartType
{
123 // The 2nd field is the end index to the buffer as mentioned above.
124 using Part
= std::pair
<PartType
, size_t>;
125 using PartVector
= mozilla::Vector
<Part
, DEFAULT_LIST_LENGTH
>;
128 * Format the list to a list of parts, and store the formatted result of
129 * UTF-16 string into buffer, and formatted parts into the vector 'parts'.
132 * https://tc39.es/ecma402/#sec-Intl.ListFormat.prototype.formatToParts
133 * https://tc39.es/ecma402/#sec-formatlisttoparts
135 template <typename Buffer
>
136 ICUResult
FormatToParts(const StringList
& list
, Buffer
& buffer
,
138 static_assert(std::is_same_v
<typename
Buffer::CharType
, char16_t
>,
139 "Currently only UTF-16 buffers are supported.");
141 mozilla::Vector
<const char16_t
*, DEFAULT_LIST_LENGTH
> u16strings
;
142 mozilla::Vector
<int32_t, DEFAULT_LIST_LENGTH
> u16stringLens
;
143 MOZ_TRY(ConvertStringListToVectors(list
, u16strings
, u16stringLens
));
145 AutoFormattedList formatted
;
146 UErrorCode status
= U_ZERO_ERROR
;
147 ulistfmt_formatStringsToResult(
148 mListFormatter
.GetConst(), u16strings
.begin(), u16stringLens
.begin(),
149 int32_t(list
.length()), formatted
.GetFormatted(), &status
);
150 if (U_FAILURE(status
)) {
151 return Err(ToICUError(status
));
154 auto spanResult
= formatted
.ToSpan();
155 if (spanResult
.isErr()) {
156 return spanResult
.propagateErr();
158 auto formattedSpan
= spanResult
.unwrap();
159 if (!FillBuffer(formattedSpan
, buffer
)) {
160 return Err(ICUError::OutOfMemory
);
163 const UFormattedValue
* value
= formatted
.Value();
165 return Err(ICUError::InternalError
);
167 return FormattedToParts(value
, buffer
.length(), parts
);
171 ListFormat() = delete;
172 explicit ListFormat(UListFormatter
* fmt
) : mListFormatter(fmt
) {}
173 ListFormat(const ListFormat
&) = delete;
174 ListFormat
& operator=(const ListFormat
&) = delete;
176 ICUPointer
<UListFormatter
> mListFormatter
=
177 ICUPointer
<UListFormatter
>(nullptr);
179 // Convert StringList to an array of type 'const char16_t*' and an array of
180 // int32 for ICU-API.
181 ICUResult
ConvertStringListToVectors(
182 const StringList
& list
,
183 mozilla::Vector
<const char16_t
*, DEFAULT_LIST_LENGTH
>& u16strings
,
184 mozilla::Vector
<int32_t, DEFAULT_LIST_LENGTH
>& u16stringLens
) const {
185 // Keep a conservative running count of overall length.
186 mozilla::CheckedInt
<int32_t> stringLengthTotal(0);
187 for (const auto& string
: list
) {
188 if (!u16strings
.append(string
.data())) {
189 return Err(ICUError::InternalError
);
192 int32_t len
= mozilla::AssertedCast
<int32_t>(string
.size());
193 if (!u16stringLens
.append(len
)) {
194 return Err(ICUError::InternalError
);
197 stringLengthTotal
+= len
;
200 // Add space for N unrealistically large conjunctions.
201 constexpr int32_t MaxConjunctionLen
= 100;
202 stringLengthTotal
+= CheckedInt
<int32_t>(list
.length()) * MaxConjunctionLen
;
203 // If the overestimate exceeds ICU length limits, don't try to format.
204 if (!stringLengthTotal
.isValid()) {
205 return Err(ICUError::OverflowError
);
211 using AutoFormattedList
=
212 AutoFormattedResult
<UFormattedList
, ulistfmt_openResult
,
213 ulistfmt_resultAsValue
, ulistfmt_closeResult
>;
215 ICUResult
FormattedToParts(const UFormattedValue
* formattedValue
,
216 size_t formattedSize
, PartVector
& parts
);
218 static UListFormatterType
ToUListFormatterType(Type type
);
219 static UListFormatterWidth
ToUListFormatterWidth(Style style
);
222 } // namespace mozilla::intl
223 #endif // intl_components_ListFormat_h_