/src/icu/source/i18n/formattedval_impl.h
Line | Count | Source (jump to first uncovered line) |
1 | | // © 2018 and later: Unicode, Inc. and others. |
2 | | // License & terms of use: http://www.unicode.org/copyright.html |
3 | | |
4 | | #ifndef __FORMVAL_IMPL_H__ |
5 | | #define __FORMVAL_IMPL_H__ |
6 | | |
7 | | #include "unicode/utypes.h" |
8 | | #if !UCONFIG_NO_FORMATTING |
9 | | |
10 | | // This file contains compliant implementations of FormattedValue which can be |
11 | | // leveraged by ICU formatters. |
12 | | // |
13 | | // Each implementation is defined in its own cpp file in order to split |
14 | | // dependencies more modularly. |
15 | | |
16 | | #include "unicode/formattedvalue.h" |
17 | | #include "capi_helper.h" |
18 | | #include "fphdlimp.h" |
19 | | #include "util.h" |
20 | | #include "uvectr32.h" |
21 | | #include "formatted_string_builder.h" |
22 | | |
23 | | |
24 | | /** |
25 | | * Represents the type of constraint for ConstrainedFieldPosition. |
26 | | * |
27 | | * Constraints are used to control the behavior of iteration in FormattedValue. |
28 | | * |
29 | | * @internal |
30 | | */ |
31 | | typedef enum UCFPosConstraintType { |
32 | | /** |
33 | | * Represents the lack of a constraint. |
34 | | * |
35 | | * This is the value of fConstraint if no "constrain" methods were called. |
36 | | * |
37 | | * @internal |
38 | | */ |
39 | | UCFPOS_CONSTRAINT_NONE = 0, |
40 | | |
41 | | /** |
42 | | * Represents that the field category is constrained. |
43 | | * |
44 | | * This is the value of fConstraint if constraintCategory was called. |
45 | | * |
46 | | * FormattedValue implementations should not change the field category |
47 | | * while this constraint is active. |
48 | | * |
49 | | * @internal |
50 | | */ |
51 | | UCFPOS_CONSTRAINT_CATEGORY, |
52 | | |
53 | | /** |
54 | | * Represents that the field and field category are constrained. |
55 | | * |
56 | | * This is the value of fConstraint if constraintField was called. |
57 | | * |
58 | | * FormattedValue implementations should not change the field or field category |
59 | | * while this constraint is active. |
60 | | * |
61 | | * @internal |
62 | | */ |
63 | | UCFPOS_CONSTRAINT_FIELD |
64 | | } UCFPosConstraintType; |
65 | | |
66 | | |
67 | | U_NAMESPACE_BEGIN |
68 | | |
69 | | |
70 | | /** |
71 | | * Implementation of FormattedValue using FieldPositionHandler to accept fields. |
72 | | * |
73 | | * TODO(ICU-20897): This class is unused. If it is not needed when fixing ICU-20897, |
74 | | * it should be deleted. |
75 | | */ |
76 | | class FormattedValueFieldPositionIteratorImpl : public UMemory, public FormattedValue { |
77 | | public: |
78 | | |
79 | | /** @param initialFieldCapacity Initially allocate space for this many fields. */ |
80 | | FormattedValueFieldPositionIteratorImpl(int32_t initialFieldCapacity, UErrorCode& status); |
81 | | |
82 | | virtual ~FormattedValueFieldPositionIteratorImpl(); |
83 | | |
84 | | // Implementation of FormattedValue (const): |
85 | | |
86 | | UnicodeString toString(UErrorCode& status) const U_OVERRIDE; |
87 | | UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE; |
88 | | Appendable& appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE; |
89 | | UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE; |
90 | | |
91 | | // Additional methods used during construction phase only (non-const): |
92 | | |
93 | | FieldPositionIteratorHandler getHandler(UErrorCode& status); |
94 | | void appendString(UnicodeString string, UErrorCode& status); |
95 | | |
96 | | /** |
97 | | * Computes the spans for duplicated values. |
98 | | * For example, if the string has fields: |
99 | | * |
100 | | * ...aa..[b.cc]..d.[bb.e.c]..a.. |
101 | | * |
102 | | * then the spans will be the bracketed regions. |
103 | | * |
104 | | * Assumes that the currently known fields are sorted |
105 | | * and all in the same category. |
106 | | */ |
107 | | void addOverlapSpans(UFieldCategory spanCategory, int8_t firstIndex, UErrorCode& status); |
108 | | |
109 | | /** |
110 | | * Sorts the fields: start index first, length second. |
111 | | */ |
112 | | void sort(); |
113 | | |
114 | | private: |
115 | | UnicodeString fString; |
116 | | UVector32 fFields; |
117 | | }; |
118 | | |
119 | | |
120 | | // Internal struct that must be exported for MSVC |
121 | | struct U_I18N_API SpanInfo { |
122 | | UFieldCategory category; |
123 | | int32_t spanValue; |
124 | | int32_t start; |
125 | | int32_t length; |
126 | | }; |
127 | | |
128 | | // Export an explicit template instantiation of the MaybeStackArray that |
129 | | // is used as a data member of CEBuffer. |
130 | | // |
131 | | // When building DLLs for Windows this is required even though |
132 | | // no direct access to the MaybeStackArray leaks out of the i18n library. |
133 | | // |
134 | | // See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples. |
135 | | // |
136 | | #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN |
137 | | template class U_I18N_API MaybeStackArray<SpanInfo, 8>; |
138 | | #endif |
139 | | |
140 | | /** |
141 | | * Implementation of FormattedValue based on FormattedStringBuilder. |
142 | | * |
143 | | * The implementation currently revolves around numbers and number fields. |
144 | | * However, it can be generalized in the future when there is a need. |
145 | | * |
146 | | * @author sffc (Shane Carr) |
147 | | */ |
148 | | // Exported as U_I18N_API for tests |
149 | | class U_I18N_API FormattedValueStringBuilderImpl : public UMemory, public FormattedValue { |
150 | | public: |
151 | | |
152 | | FormattedValueStringBuilderImpl(FormattedStringBuilder::Field numericField); |
153 | | |
154 | | virtual ~FormattedValueStringBuilderImpl(); |
155 | | |
156 | | // Implementation of FormattedValue (const): |
157 | | |
158 | | UnicodeString toString(UErrorCode& status) const U_OVERRIDE; |
159 | | UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE; |
160 | | Appendable& appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE; |
161 | | UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE; |
162 | | |
163 | | // Additional helper functions: |
164 | | UBool nextFieldPosition(FieldPosition& fp, UErrorCode& status) const; |
165 | | void getAllFieldPositions(FieldPositionIteratorHandler& fpih, UErrorCode& status) const; |
166 | 0 | inline FormattedStringBuilder& getStringRef() { |
167 | 0 | return fString; |
168 | 0 | } |
169 | 0 | inline const FormattedStringBuilder& getStringRef() const { |
170 | 0 | return fString; |
171 | 0 | } |
172 | | |
173 | | /** |
174 | | * Adds additional metadata used for span fields. |
175 | | * |
176 | | * category: the category to use for the span field. |
177 | | * spanValue: the value of the span field: index of the list item, for example. |
178 | | * start: the start position within the string of the span. -1 if unknown. |
179 | | * length: the length of the span, used to split adjacent fields. |
180 | | */ |
181 | | void appendSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status); |
182 | | void prependSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status); |
183 | | |
184 | | private: |
185 | | FormattedStringBuilder fString; |
186 | | FormattedStringBuilder::Field fNumericField; |
187 | | MaybeStackArray<SpanInfo, 8> spanIndices; |
188 | | int32_t spanIndicesCount = 0; |
189 | | |
190 | | bool nextPositionImpl(ConstrainedFieldPosition& cfpos, FormattedStringBuilder::Field numericField, UErrorCode& status) const; |
191 | | static bool isIntOrGroup(FormattedStringBuilder::Field field); |
192 | | static bool isTrimmable(FormattedStringBuilder::Field field); |
193 | | int32_t trimBack(int32_t limit) const; |
194 | | int32_t trimFront(int32_t start) const; |
195 | | }; |
196 | | |
197 | | |
198 | | // C API Helpers for FormattedValue |
199 | | // Magic number as ASCII == "UFV" |
200 | | struct UFormattedValueImpl; |
201 | | typedef IcuCApiHelper<UFormattedValue, UFormattedValueImpl, 0x55465600> UFormattedValueApiHelper; |
202 | | struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper { |
203 | | // This pointer should be set by the child class. |
204 | | FormattedValue* fFormattedValue = nullptr; |
205 | | }; |
206 | | |
207 | | |
208 | | /** Boilerplate to check for valid status before dereferencing the fData pointer. */ |
209 | | #define UPRV_FORMATTED_VALUE_METHOD_GUARD(returnExpression) \ |
210 | 0 | if (U_FAILURE(status)) { \ |
211 | 0 | return returnExpression; \ |
212 | 0 | } \ |
213 | 0 | if (fData == nullptr) { \ |
214 | 0 | status = fErrorCode; \ |
215 | 0 | return returnExpression; \ |
216 | 0 | } \ |
217 | | |
218 | | |
219 | | /** Implementation of the methods from U_FORMATTED_VALUE_SUBCLASS_AUTO. */ |
220 | | #define UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(Name) \ |
221 | | Name::Name(Name&& src) U_NOEXCEPT \ |
222 | 0 | : fData(src.fData), fErrorCode(src.fErrorCode) { \ |
223 | 0 | src.fData = nullptr; \ |
224 | 0 | src.fErrorCode = U_INVALID_STATE_ERROR; \ |
225 | 0 | } \ Unexecuted instantiation: icu_70::number::FormattedNumber::FormattedNumber(icu_70::number::FormattedNumber&&) Unexecuted instantiation: icu_70::number::FormattedNumberRange::FormattedNumberRange(icu_70::number::FormattedNumberRange&&) Unexecuted instantiation: icu_70::FormattedList::FormattedList(icu_70::FormattedList&&) |
226 | 0 | Name::~Name() { \ |
227 | 0 | delete fData; \ |
228 | 0 | fData = nullptr; \ |
229 | 0 | } \ Unexecuted instantiation: icu_70::number::FormattedNumber::~FormattedNumber() Unexecuted instantiation: icu_70::number::FormattedNumberRange::~FormattedNumberRange() Unexecuted instantiation: icu_70::FormattedList::~FormattedList() |
230 | 0 | Name& Name::operator=(Name&& src) U_NOEXCEPT { \ |
231 | 0 | delete fData; \ |
232 | 0 | fData = src.fData; \ |
233 | 0 | src.fData = nullptr; \ |
234 | 0 | fErrorCode = src.fErrorCode; \ |
235 | 0 | src.fErrorCode = U_INVALID_STATE_ERROR; \ |
236 | 0 | return *this; \ |
237 | 0 | } \ Unexecuted instantiation: icu_70::number::FormattedNumber::operator=(icu_70::number::FormattedNumber&&) Unexecuted instantiation: icu_70::number::FormattedNumberRange::operator=(icu_70::number::FormattedNumberRange&&) Unexecuted instantiation: icu_70::FormattedList::operator=(icu_70::FormattedList&&) |
238 | 0 | UnicodeString Name::toString(UErrorCode& status) const { \ |
239 | 0 | UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \ |
240 | 0 | return fData->toString(status); \ |
241 | 0 | } \ Unexecuted instantiation: icu_70::number::FormattedNumber::toString(UErrorCode&) const Unexecuted instantiation: icu_70::number::FormattedNumberRange::toString(UErrorCode&) const Unexecuted instantiation: icu_70::FormattedList::toString(UErrorCode&) const |
242 | 0 | UnicodeString Name::toTempString(UErrorCode& status) const { \ |
243 | 0 | UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \ |
244 | 0 | return fData->toTempString(status); \ |
245 | 0 | } \ Unexecuted instantiation: icu_70::number::FormattedNumber::toTempString(UErrorCode&) const Unexecuted instantiation: icu_70::number::FormattedNumberRange::toTempString(UErrorCode&) const Unexecuted instantiation: icu_70::FormattedList::toTempString(UErrorCode&) const |
246 | 0 | Appendable& Name::appendTo(Appendable& appendable, UErrorCode& status) const { \ |
247 | 0 | UPRV_FORMATTED_VALUE_METHOD_GUARD(appendable) \ |
248 | 0 | return fData->appendTo(appendable, status); \ |
249 | 0 | } \ Unexecuted instantiation: icu_70::number::FormattedNumber::appendTo(icu_70::Appendable&, UErrorCode&) const Unexecuted instantiation: icu_70::number::FormattedNumberRange::appendTo(icu_70::Appendable&, UErrorCode&) const Unexecuted instantiation: icu_70::FormattedList::appendTo(icu_70::Appendable&, UErrorCode&) const |
250 | 0 | UBool Name::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { \ |
251 | 0 | UPRV_FORMATTED_VALUE_METHOD_GUARD(false) \ |
252 | 0 | return fData->nextPosition(cfpos, status); \ |
253 | 0 | } Unexecuted instantiation: icu_70::number::FormattedNumber::nextPosition(icu_70::ConstrainedFieldPosition&, UErrorCode&) const Unexecuted instantiation: icu_70::number::FormattedNumberRange::nextPosition(icu_70::ConstrainedFieldPosition&, UErrorCode&) const Unexecuted instantiation: icu_70::FormattedList::nextPosition(icu_70::ConstrainedFieldPosition&, UErrorCode&) const |
254 | | |
255 | | |
256 | | /** Like UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL but without impl type declarations. */ |
257 | | #define UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) \ |
258 | | U_CAPI CType* U_EXPORT2 \ |
259 | | Prefix ## _openResult (UErrorCode* ec) { \ |
260 | | if (U_FAILURE(*ec)) { \ |
261 | | return nullptr; \ |
262 | | } \ |
263 | | ImplType* impl = new ImplType(); \ |
264 | | if (impl == nullptr) { \ |
265 | | *ec = U_MEMORY_ALLOCATION_ERROR; \ |
266 | | return nullptr; \ |
267 | | } \ |
268 | | return static_cast<HelperType*>(impl)->exportForC(); \ |
269 | | } \ |
270 | | U_CAPI const UFormattedValue* U_EXPORT2 \ |
271 | | Prefix ## _resultAsValue (const CType* uresult, UErrorCode* ec) { \ |
272 | | const ImplType* result = HelperType::validate(uresult, *ec); \ |
273 | | if (U_FAILURE(*ec)) { return nullptr; } \ |
274 | | return static_cast<const UFormattedValueApiHelper*>(result)->exportConstForC(); \ |
275 | | } \ |
276 | | U_CAPI void U_EXPORT2 \ |
277 | | Prefix ## _closeResult (CType* uresult) { \ |
278 | | UErrorCode localStatus = U_ZERO_ERROR; \ |
279 | | const ImplType* impl = HelperType::validate(uresult, localStatus); \ |
280 | | delete impl; \ |
281 | | } |
282 | | |
283 | | |
284 | | /** |
285 | | * Implementation of the standard methods for a UFormattedValue "subclass" C API. |
286 | | * @param CPPType The public C++ type, like FormattedList |
287 | | * @param CType The public C type, like UFormattedList |
288 | | * @param ImplType A name to use for the implementation class |
289 | | * @param HelperType A name to use for the "mixin" typedef for C API conversion |
290 | | * @param Prefix The C API prefix, like ulistfmt |
291 | | * @param MagicNumber A unique 32-bit number to use to identify this type |
292 | | */ |
293 | | #define UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(CPPType, CType, ImplType, HelperType, Prefix, MagicNumber) \ |
294 | | U_NAMESPACE_BEGIN \ |
295 | | class ImplType; \ |
296 | | typedef IcuCApiHelper<CType, ImplType, MagicNumber> HelperType; \ |
297 | | class ImplType : public UFormattedValueImpl, public HelperType { \ |
298 | | public: \ |
299 | | ImplType(); \ |
300 | | ~ImplType(); \ |
301 | | CPPType fImpl; \ |
302 | | }; \ |
303 | | ImplType::ImplType() { \ |
304 | | fFormattedValue = &fImpl; \ |
305 | | } \ |
306 | | ImplType::~ImplType() {} \ |
307 | | U_NAMESPACE_END \ |
308 | | UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) |
309 | | |
310 | | |
311 | | U_NAMESPACE_END |
312 | | |
313 | | #endif /* #if !UCONFIG_NO_FORMATTING */ |
314 | | #endif // __FORMVAL_IMPL_H__ |