Victor Chang | 7322950 | 2020-09-17 13:39:19 +0100 | [diff] [blame] | 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. |
Victor Chang | ce4bf3c | 2021-01-19 16:34:24 +0000 | [diff] [blame] | 72 | * |
| 73 | * TODO(ICU-20897): This class is unused. If it is not needed when fixing ICU-20897, |
| 74 | * it should be deleted. |
Victor Chang | 7322950 | 2020-09-17 13:39:19 +0100 | [diff] [blame] | 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 | |
Victor Chang | ce4bf3c | 2021-01-19 16:34:24 +0000 | [diff] [blame] | 120 | // Internal struct that must be exported for MSVC |
| 121 | struct U_I18N_API SpanInfo { |
| 122 | int32_t spanValue; |
| 123 | int32_t length; |
| 124 | }; |
| 125 | |
| 126 | // Export an explicit template instantiation of the MaybeStackArray that |
| 127 | // is used as a data member of CEBuffer. |
| 128 | // |
| 129 | // When building DLLs for Windows this is required even though |
| 130 | // no direct access to the MaybeStackArray leaks out of the i18n library. |
| 131 | // |
| 132 | // See digitlst.h, pluralaffix.h, datefmt.h, and others for similar examples. |
| 133 | // |
| 134 | #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN |
| 135 | template class U_I18N_API MaybeStackArray<SpanInfo, 8>; |
| 136 | #endif |
| 137 | |
Victor Chang | 7322950 | 2020-09-17 13:39:19 +0100 | [diff] [blame] | 138 | /** |
| 139 | * Implementation of FormattedValue based on FormattedStringBuilder. |
| 140 | * |
| 141 | * The implementation currently revolves around numbers and number fields. |
| 142 | * However, it can be generalized in the future when there is a need. |
| 143 | * |
| 144 | * @author sffc (Shane Carr) |
| 145 | */ |
| 146 | // Exported as U_I18N_API for tests |
| 147 | class U_I18N_API FormattedValueStringBuilderImpl : public UMemory, public FormattedValue { |
| 148 | public: |
| 149 | |
| 150 | FormattedValueStringBuilderImpl(FormattedStringBuilder::Field numericField); |
| 151 | |
| 152 | virtual ~FormattedValueStringBuilderImpl(); |
| 153 | |
| 154 | // Implementation of FormattedValue (const): |
| 155 | |
| 156 | UnicodeString toString(UErrorCode& status) const U_OVERRIDE; |
| 157 | UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE; |
| 158 | Appendable& appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE; |
| 159 | UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE; |
| 160 | |
| 161 | // Additional helper functions: |
| 162 | UBool nextFieldPosition(FieldPosition& fp, UErrorCode& status) const; |
| 163 | void getAllFieldPositions(FieldPositionIteratorHandler& fpih, UErrorCode& status) const; |
| 164 | inline FormattedStringBuilder& getStringRef() { |
| 165 | return fString; |
| 166 | } |
| 167 | inline const FormattedStringBuilder& getStringRef() const { |
| 168 | return fString; |
| 169 | } |
| 170 | |
Victor Chang | ce4bf3c | 2021-01-19 16:34:24 +0000 | [diff] [blame] | 171 | /** |
| 172 | * Adds additional metadata used for span fields. |
| 173 | * |
| 174 | * spanValue: the index of the list item, for example. |
| 175 | * length: the length of the span, used to split adjacent fields. |
| 176 | */ |
| 177 | void appendSpanInfo(int32_t spanValue, int32_t length, UErrorCode& status); |
| 178 | void prependSpanInfo(int32_t spanValue, int32_t length, UErrorCode& status); |
| 179 | |
Victor Chang | 7322950 | 2020-09-17 13:39:19 +0100 | [diff] [blame] | 180 | private: |
| 181 | FormattedStringBuilder fString; |
| 182 | FormattedStringBuilder::Field fNumericField; |
Victor Chang | ce4bf3c | 2021-01-19 16:34:24 +0000 | [diff] [blame] | 183 | MaybeStackArray<SpanInfo, 8> spanIndices; |
Victor Chang | 7322950 | 2020-09-17 13:39:19 +0100 | [diff] [blame] | 184 | |
| 185 | bool nextPositionImpl(ConstrainedFieldPosition& cfpos, FormattedStringBuilder::Field numericField, UErrorCode& status) const; |
| 186 | static bool isIntOrGroup(FormattedStringBuilder::Field field); |
Victor Chang | ce4bf3c | 2021-01-19 16:34:24 +0000 | [diff] [blame] | 187 | static bool isTrimmable(FormattedStringBuilder::Field field); |
Victor Chang | 7322950 | 2020-09-17 13:39:19 +0100 | [diff] [blame] | 188 | int32_t trimBack(int32_t limit) const; |
| 189 | int32_t trimFront(int32_t start) const; |
| 190 | }; |
| 191 | |
| 192 | |
| 193 | // C API Helpers for FormattedValue |
| 194 | // Magic number as ASCII == "UFV" |
| 195 | struct UFormattedValueImpl; |
| 196 | typedef IcuCApiHelper<UFormattedValue, UFormattedValueImpl, 0x55465600> UFormattedValueApiHelper; |
| 197 | struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper { |
| 198 | // This pointer should be set by the child class. |
| 199 | FormattedValue* fFormattedValue = nullptr; |
| 200 | }; |
| 201 | |
| 202 | |
| 203 | /** Boilerplate to check for valid status before dereferencing the fData pointer. */ |
| 204 | #define UPRV_FORMATTED_VALUE_METHOD_GUARD(returnExpression) \ |
| 205 | if (U_FAILURE(status)) { \ |
| 206 | return returnExpression; \ |
| 207 | } \ |
| 208 | if (fData == nullptr) { \ |
| 209 | status = fErrorCode; \ |
| 210 | return returnExpression; \ |
| 211 | } \ |
| 212 | |
| 213 | |
| 214 | /** Implementation of the methods from U_FORMATTED_VALUE_SUBCLASS_AUTO. */ |
| 215 | #define UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(Name) \ |
| 216 | Name::Name(Name&& src) U_NOEXCEPT \ |
| 217 | : fData(src.fData), fErrorCode(src.fErrorCode) { \ |
| 218 | src.fData = nullptr; \ |
| 219 | src.fErrorCode = U_INVALID_STATE_ERROR; \ |
| 220 | } \ |
| 221 | Name::~Name() { \ |
| 222 | delete fData; \ |
| 223 | fData = nullptr; \ |
| 224 | } \ |
| 225 | Name& Name::operator=(Name&& src) U_NOEXCEPT { \ |
| 226 | delete fData; \ |
| 227 | fData = src.fData; \ |
| 228 | src.fData = nullptr; \ |
| 229 | fErrorCode = src.fErrorCode; \ |
| 230 | src.fErrorCode = U_INVALID_STATE_ERROR; \ |
| 231 | return *this; \ |
| 232 | } \ |
| 233 | UnicodeString Name::toString(UErrorCode& status) const { \ |
| 234 | UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \ |
| 235 | return fData->toString(status); \ |
| 236 | } \ |
| 237 | UnicodeString Name::toTempString(UErrorCode& status) const { \ |
| 238 | UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \ |
| 239 | return fData->toTempString(status); \ |
| 240 | } \ |
| 241 | Appendable& Name::appendTo(Appendable& appendable, UErrorCode& status) const { \ |
| 242 | UPRV_FORMATTED_VALUE_METHOD_GUARD(appendable) \ |
| 243 | return fData->appendTo(appendable, status); \ |
| 244 | } \ |
| 245 | UBool Name::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { \ |
Victor Chang | ce4bf3c | 2021-01-19 16:34:24 +0000 | [diff] [blame] | 246 | UPRV_FORMATTED_VALUE_METHOD_GUARD(false) \ |
Victor Chang | 7322950 | 2020-09-17 13:39:19 +0100 | [diff] [blame] | 247 | return fData->nextPosition(cfpos, status); \ |
| 248 | } |
| 249 | |
| 250 | |
| 251 | /** Like UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL but without impl type declarations. */ |
| 252 | #define UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) \ |
| 253 | U_CAPI CType* U_EXPORT2 \ |
| 254 | Prefix ## _openResult (UErrorCode* ec) { \ |
| 255 | if (U_FAILURE(*ec)) { \ |
| 256 | return nullptr; \ |
| 257 | } \ |
| 258 | ImplType* impl = new ImplType(); \ |
| 259 | if (impl == nullptr) { \ |
| 260 | *ec = U_MEMORY_ALLOCATION_ERROR; \ |
| 261 | return nullptr; \ |
| 262 | } \ |
| 263 | return static_cast<HelperType*>(impl)->exportForC(); \ |
| 264 | } \ |
Victor Chang | ce4bf3c | 2021-01-19 16:34:24 +0000 | [diff] [blame] | 265 | U_CAPI const UFormattedValue* U_EXPORT2 \ |
Victor Chang | 7322950 | 2020-09-17 13:39:19 +0100 | [diff] [blame] | 266 | Prefix ## _resultAsValue (const CType* uresult, UErrorCode* ec) { \ |
| 267 | const ImplType* result = HelperType::validate(uresult, *ec); \ |
| 268 | if (U_FAILURE(*ec)) { return nullptr; } \ |
| 269 | return static_cast<const UFormattedValueApiHelper*>(result)->exportConstForC(); \ |
| 270 | } \ |
| 271 | U_CAPI void U_EXPORT2 \ |
| 272 | Prefix ## _closeResult (CType* uresult) { \ |
| 273 | UErrorCode localStatus = U_ZERO_ERROR; \ |
| 274 | const ImplType* impl = HelperType::validate(uresult, localStatus); \ |
| 275 | delete impl; \ |
| 276 | } |
| 277 | |
| 278 | |
| 279 | /** |
| 280 | * Implementation of the standard methods for a UFormattedValue "subclass" C API. |
| 281 | * @param CPPType The public C++ type, like FormattedList |
| 282 | * @param CType The public C type, like UFormattedList |
| 283 | * @param ImplType A name to use for the implementation class |
| 284 | * @param HelperType A name to use for the "mixin" typedef for C API conversion |
| 285 | * @param Prefix The C API prefix, like ulistfmt |
| 286 | * @param MagicNumber A unique 32-bit number to use to identify this type |
| 287 | */ |
| 288 | #define UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(CPPType, CType, ImplType, HelperType, Prefix, MagicNumber) \ |
| 289 | U_NAMESPACE_BEGIN \ |
| 290 | class ImplType; \ |
| 291 | typedef IcuCApiHelper<CType, ImplType, MagicNumber> HelperType; \ |
| 292 | class ImplType : public UFormattedValueImpl, public HelperType { \ |
| 293 | public: \ |
| 294 | ImplType(); \ |
| 295 | ~ImplType(); \ |
| 296 | CPPType fImpl; \ |
| 297 | }; \ |
| 298 | ImplType::ImplType() { \ |
| 299 | fFormattedValue = &fImpl; \ |
| 300 | } \ |
| 301 | ImplType::~ImplType() {} \ |
| 302 | U_NAMESPACE_END \ |
| 303 | UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) |
| 304 | |
| 305 | |
| 306 | U_NAMESPACE_END |
| 307 | |
| 308 | #endif /* #if !UCONFIG_NO_FORMATTING */ |
| 309 | #endif // __FORMVAL_IMPL_H__ |