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 */
31typedef 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
67U_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 */
76class FormattedValueFieldPositionIteratorImpl : public UMemory, public FormattedValue {
77public:
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
114private:
115    UnicodeString fString;
116    UVector32 fFields;
117};
118
119
120// Internal struct that must be exported for MSVC
121struct 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
137template 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
149class U_I18N_API FormattedValueStringBuilderImpl : public UMemory, public FormattedValue {
150public:
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    inline FormattedStringBuilder& getStringRef() {
167        return fString;
168    }
169    inline const FormattedStringBuilder& getStringRef() const {
170        return fString;
171    }
172    void resetString();
173
174    /**
175     * Adds additional metadata used for span fields.
176     *
177     * category: the category to use for the span field.
178     * spanValue: the value of the span field: index of the list item, for example.
179     * start: the start position within the string of the span. -1 if unknown.
180     * length: the length of the span, used to split adjacent fields.
181     */
182    void appendSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status);
183    void prependSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status);
184
185private:
186    FormattedStringBuilder fString;
187    FormattedStringBuilder::Field fNumericField;
188    MaybeStackArray<SpanInfo, 8> spanIndices;
189    int32_t spanIndicesCount = 0;
190
191    bool nextPositionImpl(ConstrainedFieldPosition& cfpos, FormattedStringBuilder::Field numericField, UErrorCode& status) const;
192    static bool isIntOrGroup(FormattedStringBuilder::Field field);
193    static bool isTrimmable(FormattedStringBuilder::Field field);
194    int32_t trimBack(int32_t limit) const;
195    int32_t trimFront(int32_t start) const;
196};
197
198
199// C API Helpers for FormattedValue
200// Magic number as ASCII == "UFV"
201struct UFormattedValueImpl;
202typedef IcuCApiHelper<UFormattedValue, UFormattedValueImpl, 0x55465600> UFormattedValueApiHelper;
203struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper {
204    // This pointer should be set by the child class.
205    FormattedValue* fFormattedValue = nullptr;
206};
207
208
209/** Boilerplate to check for valid status before dereferencing the fData pointer. */
210#define UPRV_FORMATTED_VALUE_METHOD_GUARD(returnExpression) \
211    if (U_FAILURE(status)) { \
212        return returnExpression; \
213    } \
214    if (fData == nullptr) { \
215        status = fErrorCode; \
216        return returnExpression; \
217    } \
218
219
220/** Implementation of the methods from U_FORMATTED_VALUE_SUBCLASS_AUTO. */
221#define UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(Name) \
222    Name::Name(Name&& src) U_NOEXCEPT \
223            : fData(src.fData), fErrorCode(src.fErrorCode) { \
224        src.fData = nullptr; \
225        src.fErrorCode = U_INVALID_STATE_ERROR; \
226    } \
227    Name::~Name() { \
228        delete fData; \
229        fData = nullptr; \
230    } \
231    Name& Name::operator=(Name&& src) U_NOEXCEPT { \
232        delete fData; \
233        fData = src.fData; \
234        src.fData = nullptr; \
235        fErrorCode = src.fErrorCode; \
236        src.fErrorCode = U_INVALID_STATE_ERROR; \
237        return *this; \
238    } \
239    UnicodeString Name::toString(UErrorCode& status) const { \
240        UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \
241        return fData->toString(status); \
242    } \
243    UnicodeString Name::toTempString(UErrorCode& status) const { \
244        UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \
245        return fData->toTempString(status); \
246    } \
247    Appendable& Name::appendTo(Appendable& appendable, UErrorCode& status) const { \
248        UPRV_FORMATTED_VALUE_METHOD_GUARD(appendable) \
249        return fData->appendTo(appendable, status); \
250    } \
251    UBool Name::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { \
252        UPRV_FORMATTED_VALUE_METHOD_GUARD(false) \
253        return fData->nextPosition(cfpos, status); \
254    }
255
256
257/** Like UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL but without impl type declarations. */
258#define UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) \
259    U_CAPI CType* U_EXPORT2 \
260    Prefix ## _openResult (UErrorCode* ec) { \
261        if (U_FAILURE(*ec)) { \
262            return nullptr; \
263        } \
264        ImplType* impl = new ImplType(); \
265        if (impl == nullptr) { \
266            *ec = U_MEMORY_ALLOCATION_ERROR; \
267            return nullptr; \
268        } \
269        return static_cast<HelperType*>(impl)->exportForC(); \
270    } \
271    U_CAPI const UFormattedValue* U_EXPORT2 \
272    Prefix ## _resultAsValue (const CType* uresult, UErrorCode* ec) { \
273        const ImplType* result = HelperType::validate(uresult, *ec); \
274        if (U_FAILURE(*ec)) { return nullptr; } \
275        return static_cast<const UFormattedValueApiHelper*>(result)->exportConstForC(); \
276    } \
277    U_CAPI void U_EXPORT2 \
278    Prefix ## _closeResult (CType* uresult) { \
279        UErrorCode localStatus = U_ZERO_ERROR; \
280        const ImplType* impl = HelperType::validate(uresult, localStatus); \
281        delete impl; \
282    }
283
284
285/**
286 * Implementation of the standard methods for a UFormattedValue "subclass" C API.
287 * @param CPPType The public C++ type, like FormattedList
288 * @param CType The public C type, like UFormattedList
289 * @param ImplType A name to use for the implementation class
290 * @param HelperType A name to use for the "mixin" typedef for C API conversion
291 * @param Prefix The C API prefix, like ulistfmt
292 * @param MagicNumber A unique 32-bit number to use to identify this type
293 */
294#define UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(CPPType, CType, ImplType, HelperType, Prefix, MagicNumber) \
295    U_NAMESPACE_BEGIN \
296    class ImplType; \
297    typedef IcuCApiHelper<CType, ImplType, MagicNumber> HelperType; \
298    class ImplType : public UFormattedValueImpl, public HelperType { \
299    public: \
300        ImplType(); \
301        ~ImplType(); \
302        CPPType fImpl; \
303    }; \
304    ImplType::ImplType() { \
305        fFormattedValue = &fImpl; \
306    } \
307    ImplType::~ImplType() {} \
308    U_NAMESPACE_END \
309    UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix)
310
311
312U_NAMESPACE_END
313
314#endif /* #if !UCONFIG_NO_FORMATTING */
315#endif // __FORMVAL_IMPL_H__
316