/src/icu/source/i18n/number_modifiers.h
Line | Count | Source (jump to first uncovered line) |
1 | | // © 2017 and later: Unicode, Inc. and others. |
2 | | // License & terms of use: http://www.unicode.org/copyright.html |
3 | | |
4 | | #include "unicode/utypes.h" |
5 | | |
6 | | #if !UCONFIG_NO_FORMATTING |
7 | | #ifndef __NUMBER_MODIFIERS_H__ |
8 | | #define __NUMBER_MODIFIERS_H__ |
9 | | |
10 | | #include <algorithm> |
11 | | #include <cstdint> |
12 | | #include "unicode/uniset.h" |
13 | | #include "unicode/simpleformatter.h" |
14 | | #include "standardplural.h" |
15 | | #include "formatted_string_builder.h" |
16 | | #include "number_types.h" |
17 | | |
18 | | U_NAMESPACE_BEGIN namespace number { |
19 | | namespace impl { |
20 | | |
21 | | /** |
22 | | * The canonical implementation of {@link Modifier}, containing a prefix and suffix string. |
23 | | * TODO: This is not currently being used by real code and could be removed. |
24 | | */ |
25 | | class U_I18N_API ConstantAffixModifier : public Modifier, public UObject { |
26 | | public: |
27 | | ConstantAffixModifier(const UnicodeString &prefix, const UnicodeString &suffix, Field field, |
28 | | bool strong) |
29 | 0 | : fPrefix(prefix), fSuffix(suffix), fField(field), fStrong(strong) {} |
30 | | |
31 | | int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, |
32 | | UErrorCode &status) const U_OVERRIDE; |
33 | | |
34 | | int32_t getPrefixLength() const U_OVERRIDE; |
35 | | |
36 | | int32_t getCodePointCount() const U_OVERRIDE; |
37 | | |
38 | | bool isStrong() const U_OVERRIDE; |
39 | | |
40 | | bool containsField(Field field) const U_OVERRIDE; |
41 | | |
42 | | void getParameters(Parameters& output) const U_OVERRIDE; |
43 | | |
44 | | bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; |
45 | | |
46 | | private: |
47 | | UnicodeString fPrefix; |
48 | | UnicodeString fSuffix; |
49 | | Field fField; |
50 | | bool fStrong; |
51 | | }; |
52 | | |
53 | | /** |
54 | | * The second primary implementation of {@link Modifier}, this one consuming a {@link SimpleFormatter} |
55 | | * pattern. |
56 | | */ |
57 | | class U_I18N_API SimpleModifier : public Modifier, public UMemory { |
58 | | public: |
59 | | SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong); |
60 | | |
61 | | SimpleModifier(const SimpleFormatter &simpleFormatter, Field field, bool strong, |
62 | | const Modifier::Parameters parameters); |
63 | | |
64 | | // Default constructor for LongNameHandler.h |
65 | | SimpleModifier(); |
66 | | |
67 | | int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, |
68 | | UErrorCode &status) const U_OVERRIDE; |
69 | | |
70 | | int32_t getPrefixLength() const U_OVERRIDE; |
71 | | |
72 | | int32_t getCodePointCount() const U_OVERRIDE; |
73 | | |
74 | | bool isStrong() const U_OVERRIDE; |
75 | | |
76 | | bool containsField(Field field) const U_OVERRIDE; |
77 | | |
78 | | void getParameters(Parameters& output) const U_OVERRIDE; |
79 | | |
80 | | bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; |
81 | | |
82 | | /** |
83 | | * TODO: This belongs in SimpleFormatterImpl. The only reason I haven't moved it there yet is because |
84 | | * FormattedStringBuilder is an internal class and SimpleFormatterImpl feels like it should not depend on it. |
85 | | * |
86 | | * <p> |
87 | | * Formats a value that is already stored inside the StringBuilder <code>result</code> between the indices |
88 | | * <code>startIndex</code> and <code>endIndex</code> by inserting characters before the start index and after the |
89 | | * end index. |
90 | | * |
91 | | * <p> |
92 | | * This is well-defined only for patterns with exactly one argument. |
93 | | * |
94 | | * @param result |
95 | | * The StringBuilder containing the value argument. |
96 | | * @param startIndex |
97 | | * The left index of the value within the string builder. |
98 | | * @param endIndex |
99 | | * The right index of the value within the string builder. |
100 | | * @return The number of characters (UTF-16 code points) that were added to the StringBuilder. |
101 | | */ |
102 | | int32_t |
103 | | formatAsPrefixSuffix(FormattedStringBuilder& result, int32_t startIndex, int32_t endIndex, |
104 | | UErrorCode& status) const; |
105 | | |
106 | | /** |
107 | | * TODO: Like above, this belongs with the rest of the SimpleFormatterImpl code. |
108 | | * I put it here so that the SimpleFormatter uses in FormattedStringBuilder are near each other. |
109 | | * |
110 | | * <p> |
111 | | * Applies the compiled two-argument pattern to the FormattedStringBuilder. |
112 | | * |
113 | | * <p> |
114 | | * This method is optimized for the case where the prefix and suffix are often empty, such as |
115 | | * in the range pattern like "{0}-{1}". |
116 | | */ |
117 | | static int32_t |
118 | | formatTwoArgPattern(const SimpleFormatter& compiled, FormattedStringBuilder& result, |
119 | | int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength, |
120 | | Field field, UErrorCode& status); |
121 | | |
122 | | private: |
123 | | UnicodeString fCompiledPattern; |
124 | | Field fField; |
125 | | bool fStrong = false; |
126 | | int32_t fPrefixLength = 0; |
127 | | int32_t fSuffixOffset = -1; |
128 | | int32_t fSuffixLength = 0; |
129 | | Modifier::Parameters fParameters; |
130 | | }; |
131 | | |
132 | | /** |
133 | | * An implementation of {@link Modifier} that allows for multiple types of fields in the same modifier. Constructed |
134 | | * based on the contents of two {@link FormattedStringBuilder} instances (one for the prefix, one for the suffix). |
135 | | */ |
136 | | class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory { |
137 | | public: |
138 | | ConstantMultiFieldModifier( |
139 | | const FormattedStringBuilder &prefix, |
140 | | const FormattedStringBuilder &suffix, |
141 | | bool overwrite, |
142 | | bool strong, |
143 | | const Modifier::Parameters parameters) |
144 | | : fPrefix(prefix), |
145 | | fSuffix(suffix), |
146 | | fOverwrite(overwrite), |
147 | | fStrong(strong), |
148 | 0 | fParameters(parameters) {} |
149 | | |
150 | | ConstantMultiFieldModifier( |
151 | | const FormattedStringBuilder &prefix, |
152 | | const FormattedStringBuilder &suffix, |
153 | | bool overwrite, |
154 | | bool strong) |
155 | 0 | : fPrefix(prefix), |
156 | 0 | fSuffix(suffix), |
157 | 0 | fOverwrite(overwrite), |
158 | 0 | fStrong(strong) {} |
159 | | |
160 | | int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, |
161 | | UErrorCode &status) const U_OVERRIDE; |
162 | | |
163 | | int32_t getPrefixLength() const U_OVERRIDE; |
164 | | |
165 | | int32_t getCodePointCount() const U_OVERRIDE; |
166 | | |
167 | | bool isStrong() const U_OVERRIDE; |
168 | | |
169 | | bool containsField(Field field) const U_OVERRIDE; |
170 | | |
171 | | void getParameters(Parameters& output) const U_OVERRIDE; |
172 | | |
173 | | bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; |
174 | | |
175 | | protected: |
176 | | // NOTE: In Java, these are stored as array pointers. In C++, the FormattedStringBuilder is stored by |
177 | | // value and is treated internally as immutable. |
178 | | FormattedStringBuilder fPrefix; |
179 | | FormattedStringBuilder fSuffix; |
180 | | bool fOverwrite; |
181 | | bool fStrong; |
182 | | Modifier::Parameters fParameters; |
183 | | }; |
184 | | |
185 | | /** Identical to {@link ConstantMultiFieldModifier}, but supports currency spacing. */ |
186 | | class U_I18N_API CurrencySpacingEnabledModifier : public ConstantMultiFieldModifier { |
187 | | public: |
188 | | /** Safe code path */ |
189 | | CurrencySpacingEnabledModifier( |
190 | | const FormattedStringBuilder &prefix, |
191 | | const FormattedStringBuilder &suffix, |
192 | | bool overwrite, |
193 | | bool strong, |
194 | | const DecimalFormatSymbols &symbols, |
195 | | UErrorCode &status); |
196 | | |
197 | | int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, |
198 | | UErrorCode &status) const U_OVERRIDE; |
199 | | |
200 | | /** Unsafe code path */ |
201 | | static int32_t |
202 | | applyCurrencySpacing(FormattedStringBuilder &output, int32_t prefixStart, int32_t prefixLen, |
203 | | int32_t suffixStart, int32_t suffixLen, const DecimalFormatSymbols &symbols, |
204 | | UErrorCode &status); |
205 | | |
206 | | private: |
207 | | UnicodeSet fAfterPrefixUnicodeSet; |
208 | | UnicodeString fAfterPrefixInsert; |
209 | | UnicodeSet fBeforeSuffixUnicodeSet; |
210 | | UnicodeString fBeforeSuffixInsert; |
211 | | |
212 | | enum EAffix { |
213 | | PREFIX, SUFFIX |
214 | | }; |
215 | | |
216 | | enum EPosition { |
217 | | IN_CURRENCY, IN_NUMBER |
218 | | }; |
219 | | |
220 | | /** Unsafe code path */ |
221 | | static int32_t applyCurrencySpacingAffix(FormattedStringBuilder &output, int32_t index, EAffix affix, |
222 | | const DecimalFormatSymbols &symbols, UErrorCode &status); |
223 | | |
224 | | static UnicodeSet |
225 | | getUnicodeSet(const DecimalFormatSymbols &symbols, EPosition position, EAffix affix, |
226 | | UErrorCode &status); |
227 | | |
228 | | static UnicodeString |
229 | | getInsertString(const DecimalFormatSymbols &symbols, EAffix affix, UErrorCode &status); |
230 | | }; |
231 | | |
232 | | /** A Modifier that does not do anything. */ |
233 | | class U_I18N_API EmptyModifier : public Modifier, public UMemory { |
234 | | public: |
235 | 0 | explicit EmptyModifier(bool isStrong) : fStrong(isStrong) {} |
236 | | |
237 | | int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, |
238 | 0 | UErrorCode &status) const U_OVERRIDE { |
239 | 0 | (void)output; |
240 | 0 | (void)leftIndex; |
241 | 0 | (void)rightIndex; |
242 | 0 | (void)status; |
243 | 0 | return 0; |
244 | 0 | } |
245 | | |
246 | 0 | int32_t getPrefixLength() const U_OVERRIDE { |
247 | 0 | return 0; |
248 | 0 | } |
249 | | |
250 | 0 | int32_t getCodePointCount() const U_OVERRIDE { |
251 | 0 | return 0; |
252 | 0 | } |
253 | | |
254 | 0 | bool isStrong() const U_OVERRIDE { |
255 | 0 | return fStrong; |
256 | 0 | } |
257 | | |
258 | 0 | bool containsField(Field field) const U_OVERRIDE { |
259 | 0 | (void)field; |
260 | 0 | return false; |
261 | 0 | } |
262 | | |
263 | 0 | void getParameters(Parameters& output) const U_OVERRIDE { |
264 | 0 | output.obj = nullptr; |
265 | 0 | } |
266 | | |
267 | 0 | bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE { |
268 | 0 | return other.getCodePointCount() == 0; |
269 | 0 | } |
270 | | |
271 | | private: |
272 | | bool fStrong; |
273 | | }; |
274 | | |
275 | | /** |
276 | | * This implementation of ModifierStore adopts Modifier pointers. |
277 | | */ |
278 | | class U_I18N_API AdoptingModifierStore : public ModifierStore, public UMemory { |
279 | | public: |
280 | | virtual ~AdoptingModifierStore(); |
281 | | |
282 | | static constexpr StandardPlural::Form DEFAULT_STANDARD_PLURAL = StandardPlural::OTHER; |
283 | | |
284 | 0 | AdoptingModifierStore() = default; |
285 | | |
286 | | // No copying! |
287 | | AdoptingModifierStore(const AdoptingModifierStore &other) = delete; |
288 | | |
289 | | /** |
290 | | * Sets the Modifier with the specified signum and plural form. |
291 | | */ |
292 | 0 | void adoptModifier(Signum signum, StandardPlural::Form plural, const Modifier *mod) { |
293 | 0 | U_ASSERT(mods[getModIndex(signum, plural)] == nullptr); |
294 | 0 | mods[getModIndex(signum, plural)] = mod; |
295 | 0 | } |
296 | | |
297 | | /** |
298 | | * Sets the Modifier with the specified signum. |
299 | | * The modifier will apply to all plural forms. |
300 | | */ |
301 | 0 | void adoptModifierWithoutPlural(Signum signum, const Modifier *mod) { |
302 | 0 | U_ASSERT(mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] == nullptr); |
303 | 0 | mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)] = mod; |
304 | 0 | } |
305 | | |
306 | | /** Returns a reference to the modifier; no ownership change. */ |
307 | 0 | const Modifier *getModifier(Signum signum, StandardPlural::Form plural) const U_OVERRIDE { |
308 | 0 | const Modifier* modifier = mods[getModIndex(signum, plural)]; |
309 | 0 | if (modifier == nullptr && plural != DEFAULT_STANDARD_PLURAL) { |
310 | 0 | modifier = mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)]; |
311 | 0 | } |
312 | 0 | return modifier; |
313 | 0 | } |
314 | | |
315 | | /** Returns a reference to the modifier; no ownership change. */ |
316 | 0 | const Modifier *getModifierWithoutPlural(Signum signum) const { |
317 | 0 | return mods[getModIndex(signum, DEFAULT_STANDARD_PLURAL)]; |
318 | 0 | } |
319 | | |
320 | | private: |
321 | | // NOTE: mods is zero-initialized (to nullptr) |
322 | | const Modifier *mods[4 * StandardPlural::COUNT] = {}; |
323 | | |
324 | 0 | inline static int32_t getModIndex(Signum signum, StandardPlural::Form plural) { |
325 | 0 | U_ASSERT(signum >= 0 && signum < SIGNUM_COUNT); |
326 | 0 | U_ASSERT(plural >= 0 && plural < StandardPlural::COUNT); |
327 | 0 | return static_cast<int32_t>(plural) * SIGNUM_COUNT + signum; |
328 | 0 | } |
329 | | }; |
330 | | |
331 | | } // namespace impl |
332 | | } // namespace number |
333 | | U_NAMESPACE_END |
334 | | |
335 | | |
336 | | #endif //__NUMBER_MODIFIERS_H__ |
337 | | |
338 | | #endif /* #if !UCONFIG_NO_FORMATTING */ |