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. |
| 72 | */ |
| 73 | class FormattedValueFieldPositionIteratorImpl : public UMemory, public FormattedValue { |
| 74 | public: |
| 75 | |
| 76 | /** @param initialFieldCapacity Initially allocate space for this many fields. */ |
| 77 | FormattedValueFieldPositionIteratorImpl(int32_t initialFieldCapacity, UErrorCode& status); |
| 78 | |
| 79 | virtual ~FormattedValueFieldPositionIteratorImpl(); |
| 80 | |
| 81 | // Implementation of FormattedValue (const): |
| 82 | |
| 83 | UnicodeString toString(UErrorCode& status) const U_OVERRIDE; |
| 84 | UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE; |
| 85 | Appendable& appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE; |
| 86 | UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE; |
| 87 | |
| 88 | // Additional methods used during construction phase only (non-const): |
| 89 | |
| 90 | FieldPositionIteratorHandler getHandler(UErrorCode& status); |
| 91 | void appendString(UnicodeString string, UErrorCode& status); |
| 92 | |
| 93 | /** |
| 94 | * Computes the spans for duplicated values. |
| 95 | * For example, if the string has fields: |
| 96 | * |
| 97 | * ...aa..[b.cc]..d.[bb.e.c]..a.. |
| 98 | * |
| 99 | * then the spans will be the bracketed regions. |
| 100 | * |
| 101 | * Assumes that the currently known fields are sorted |
| 102 | * and all in the same category. |
| 103 | */ |
| 104 | void addOverlapSpans(UFieldCategory spanCategory, int8_t firstIndex, UErrorCode& status); |
| 105 | |
| 106 | /** |
| 107 | * Sorts the fields: start index first, length second. |
| 108 | */ |
| 109 | void sort(); |
| 110 | |
| 111 | private: |
| 112 | UnicodeString fString; |
| 113 | UVector32 fFields; |
| 114 | }; |
| 115 | |
| 116 | |
| 117 | /** |
| 118 | * Implementation of FormattedValue based on FormattedStringBuilder. |
| 119 | * |
| 120 | * The implementation currently revolves around numbers and number fields. |
| 121 | * However, it can be generalized in the future when there is a need. |
| 122 | * |
| 123 | * @author sffc (Shane Carr) |
| 124 | */ |
| 125 | // Exported as U_I18N_API for tests |
| 126 | class U_I18N_API FormattedValueStringBuilderImpl : public UMemory, public FormattedValue { |
| 127 | public: |
| 128 | |
| 129 | FormattedValueStringBuilderImpl(FormattedStringBuilder::Field numericField); |
| 130 | |
| 131 | virtual ~FormattedValueStringBuilderImpl(); |
| 132 | |
| 133 | // Implementation of FormattedValue (const): |
| 134 | |
| 135 | UnicodeString toString(UErrorCode& status) const U_OVERRIDE; |
| 136 | UnicodeString toTempString(UErrorCode& status) const U_OVERRIDE; |
| 137 | Appendable& appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE; |
| 138 | UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE; |
| 139 | |
| 140 | // Additional helper functions: |
| 141 | UBool nextFieldPosition(FieldPosition& fp, UErrorCode& status) const; |
| 142 | void getAllFieldPositions(FieldPositionIteratorHandler& fpih, UErrorCode& status) const; |
| 143 | inline FormattedStringBuilder& getStringRef() { |
| 144 | return fString; |
| 145 | } |
| 146 | inline const FormattedStringBuilder& getStringRef() const { |
| 147 | return fString; |
| 148 | } |
| 149 | |
| 150 | private: |
| 151 | FormattedStringBuilder fString; |
| 152 | FormattedStringBuilder::Field fNumericField; |
| 153 | |
| 154 | bool nextPositionImpl(ConstrainedFieldPosition& cfpos, FormattedStringBuilder::Field numericField, UErrorCode& status) const; |
| 155 | static bool isIntOrGroup(FormattedStringBuilder::Field field); |
| 156 | static bool isNumericField(FormattedStringBuilder::Field field); |
| 157 | int32_t trimBack(int32_t limit) const; |
| 158 | int32_t trimFront(int32_t start) const; |
| 159 | }; |
| 160 | |
| 161 | |
| 162 | // C API Helpers for FormattedValue |
| 163 | // Magic number as ASCII == "UFV" |
| 164 | struct UFormattedValueImpl; |
| 165 | typedef IcuCApiHelper<UFormattedValue, UFormattedValueImpl, 0x55465600> UFormattedValueApiHelper; |
| 166 | struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper { |
| 167 | // This pointer should be set by the child class. |
| 168 | FormattedValue* fFormattedValue = nullptr; |
| 169 | }; |
| 170 | |
| 171 | |
| 172 | /** Boilerplate to check for valid status before dereferencing the fData pointer. */ |
| 173 | #define UPRV_FORMATTED_VALUE_METHOD_GUARD(returnExpression) \ |
| 174 | if (U_FAILURE(status)) { \ |
| 175 | return returnExpression; \ |
| 176 | } \ |
| 177 | if (fData == nullptr) { \ |
| 178 | status = fErrorCode; \ |
| 179 | return returnExpression; \ |
| 180 | } \ |
| 181 | |
| 182 | |
| 183 | /** Implementation of the methods from U_FORMATTED_VALUE_SUBCLASS_AUTO. */ |
| 184 | #define UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(Name) \ |
| 185 | Name::Name(Name&& src) U_NOEXCEPT \ |
| 186 | : fData(src.fData), fErrorCode(src.fErrorCode) { \ |
| 187 | src.fData = nullptr; \ |
| 188 | src.fErrorCode = U_INVALID_STATE_ERROR; \ |
| 189 | } \ |
| 190 | Name::~Name() { \ |
| 191 | delete fData; \ |
| 192 | fData = nullptr; \ |
| 193 | } \ |
| 194 | Name& Name::operator=(Name&& src) U_NOEXCEPT { \ |
| 195 | delete fData; \ |
| 196 | fData = src.fData; \ |
| 197 | src.fData = nullptr; \ |
| 198 | fErrorCode = src.fErrorCode; \ |
| 199 | src.fErrorCode = U_INVALID_STATE_ERROR; \ |
| 200 | return *this; \ |
| 201 | } \ |
| 202 | UnicodeString Name::toString(UErrorCode& status) const { \ |
| 203 | UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \ |
| 204 | return fData->toString(status); \ |
| 205 | } \ |
| 206 | UnicodeString Name::toTempString(UErrorCode& status) const { \ |
| 207 | UPRV_FORMATTED_VALUE_METHOD_GUARD(ICU_Utility::makeBogusString()) \ |
| 208 | return fData->toTempString(status); \ |
| 209 | } \ |
| 210 | Appendable& Name::appendTo(Appendable& appendable, UErrorCode& status) const { \ |
| 211 | UPRV_FORMATTED_VALUE_METHOD_GUARD(appendable) \ |
| 212 | return fData->appendTo(appendable, status); \ |
| 213 | } \ |
| 214 | UBool Name::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { \ |
| 215 | UPRV_FORMATTED_VALUE_METHOD_GUARD(FALSE) \ |
| 216 | return fData->nextPosition(cfpos, status); \ |
| 217 | } |
| 218 | |
| 219 | |
| 220 | /** Like UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL but without impl type declarations. */ |
| 221 | #define UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) \ |
| 222 | U_CAPI CType* U_EXPORT2 \ |
| 223 | Prefix ## _openResult (UErrorCode* ec) { \ |
| 224 | if (U_FAILURE(*ec)) { \ |
| 225 | return nullptr; \ |
| 226 | } \ |
| 227 | ImplType* impl = new ImplType(); \ |
| 228 | if (impl == nullptr) { \ |
| 229 | *ec = U_MEMORY_ALLOCATION_ERROR; \ |
| 230 | return nullptr; \ |
| 231 | } \ |
| 232 | return static_cast<HelperType*>(impl)->exportForC(); \ |
| 233 | } \ |
| 234 | U_DRAFT const UFormattedValue* U_EXPORT2 \ |
| 235 | Prefix ## _resultAsValue (const CType* uresult, UErrorCode* ec) { \ |
| 236 | const ImplType* result = HelperType::validate(uresult, *ec); \ |
| 237 | if (U_FAILURE(*ec)) { return nullptr; } \ |
| 238 | return static_cast<const UFormattedValueApiHelper*>(result)->exportConstForC(); \ |
| 239 | } \ |
| 240 | U_CAPI void U_EXPORT2 \ |
| 241 | Prefix ## _closeResult (CType* uresult) { \ |
| 242 | UErrorCode localStatus = U_ZERO_ERROR; \ |
| 243 | const ImplType* impl = HelperType::validate(uresult, localStatus); \ |
| 244 | delete impl; \ |
| 245 | } |
| 246 | |
| 247 | |
| 248 | /** |
| 249 | * Implementation of the standard methods for a UFormattedValue "subclass" C API. |
| 250 | * @param CPPType The public C++ type, like FormattedList |
| 251 | * @param CType The public C type, like UFormattedList |
| 252 | * @param ImplType A name to use for the implementation class |
| 253 | * @param HelperType A name to use for the "mixin" typedef for C API conversion |
| 254 | * @param Prefix The C API prefix, like ulistfmt |
| 255 | * @param MagicNumber A unique 32-bit number to use to identify this type |
| 256 | */ |
| 257 | #define UPRV_FORMATTED_VALUE_CAPI_AUTO_IMPL(CPPType, CType, ImplType, HelperType, Prefix, MagicNumber) \ |
| 258 | U_NAMESPACE_BEGIN \ |
| 259 | class ImplType; \ |
| 260 | typedef IcuCApiHelper<CType, ImplType, MagicNumber> HelperType; \ |
| 261 | class ImplType : public UFormattedValueImpl, public HelperType { \ |
| 262 | public: \ |
| 263 | ImplType(); \ |
| 264 | ~ImplType(); \ |
| 265 | CPPType fImpl; \ |
| 266 | }; \ |
| 267 | ImplType::ImplType() { \ |
| 268 | fFormattedValue = &fImpl; \ |
| 269 | } \ |
| 270 | ImplType::~ImplType() {} \ |
| 271 | U_NAMESPACE_END \ |
| 272 | UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(CType, ImplType, HelperType, Prefix) |
| 273 | |
| 274 | |
| 275 | U_NAMESPACE_END |
| 276 | |
| 277 | #endif /* #if !UCONFIG_NO_FORMATTING */ |
| 278 | #endif // __FORMVAL_IMPL_H__ |