• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2018 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 
8 // This file contains one implementation of FormattedValue.
9 // Other independent implementations should go into their own cpp file for
10 // better dependency modularization.
11 
12 #include "unicode/ustring.h"
13 #include "formattedval_impl.h"
14 #include "number_types.h"
15 #include "formatted_string_builder.h"
16 #include "number_utils.h"
17 #include "static_unicode_sets.h"
18 #include "unicode/listformatter.h"
19 
20 U_NAMESPACE_BEGIN
21 
22 
23 typedef FormattedStringBuilder::Field Field;
24 
25 
FormattedValueStringBuilderImpl(Field numericField)26 FormattedValueStringBuilderImpl::FormattedValueStringBuilderImpl(Field numericField)
27         : fNumericField(numericField) {
28 }
29 
~FormattedValueStringBuilderImpl()30 FormattedValueStringBuilderImpl::~FormattedValueStringBuilderImpl() {
31 }
32 
33 
toString(UErrorCode &) const34 UnicodeString FormattedValueStringBuilderImpl::toString(UErrorCode&) const {
35     return fString.toUnicodeString();
36 }
37 
toTempString(UErrorCode &) const38 UnicodeString FormattedValueStringBuilderImpl::toTempString(UErrorCode&) const {
39     return fString.toTempUnicodeString();
40 }
41 
appendTo(Appendable & appendable,UErrorCode &) const42 Appendable& FormattedValueStringBuilderImpl::appendTo(Appendable& appendable, UErrorCode&) const {
43     appendable.appendString(fString.chars(), fString.length());
44     return appendable;
45 }
46 
nextPosition(ConstrainedFieldPosition & cfpos,UErrorCode & status) const47 UBool FormattedValueStringBuilderImpl::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const {
48     // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
49     return nextPositionImpl(cfpos, fNumericField, status) ? true : false;
50 }
51 
nextFieldPosition(FieldPosition & fp,UErrorCode & status) const52 UBool FormattedValueStringBuilderImpl::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const {
53     int32_t rawField = fp.getField();
54 
55     if (rawField == FieldPosition::DONT_CARE) {
56         return false;
57     }
58 
59     if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) {
60         status = U_ILLEGAL_ARGUMENT_ERROR;
61         return false;
62     }
63 
64     ConstrainedFieldPosition cfpos;
65     cfpos.constrainField(UFIELD_CATEGORY_NUMBER, rawField);
66     cfpos.setState(UFIELD_CATEGORY_NUMBER, rawField, fp.getBeginIndex(), fp.getEndIndex());
67     if (nextPositionImpl(cfpos, kUndefinedField, status)) {
68         fp.setBeginIndex(cfpos.getStart());
69         fp.setEndIndex(cfpos.getLimit());
70         return true;
71     }
72 
73     // Special case: fraction should start after integer if fraction is not present
74     if (rawField == UNUM_FRACTION_FIELD && fp.getEndIndex() == 0) {
75         bool inside = false;
76         int32_t i = fString.fZero;
77         for (; i < fString.fZero + fString.fLength; i++) {
78             if (isIntOrGroup(fString.getFieldPtr()[i]) || fString.getFieldPtr()[i] == Field(UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD)) {
79                 inside = true;
80             } else if (inside) {
81                 break;
82             }
83         }
84         fp.setBeginIndex(i - fString.fZero);
85         fp.setEndIndex(i - fString.fZero);
86     }
87 
88     return false;
89 }
90 
getAllFieldPositions(FieldPositionIteratorHandler & fpih,UErrorCode & status) const91 void FormattedValueStringBuilderImpl::getAllFieldPositions(FieldPositionIteratorHandler& fpih,
92                                                UErrorCode& status) const {
93     ConstrainedFieldPosition cfpos;
94     while (nextPositionImpl(cfpos, kUndefinedField, status)) {
95         fpih.addAttribute(cfpos.getField(), cfpos.getStart(), cfpos.getLimit());
96     }
97 }
98 
resetString()99 void FormattedValueStringBuilderImpl::resetString() {
100     fString.clear();
101     spanIndicesCount = 0;
102 }
103 
104 // Signal the end of the string using a field that doesn't exist and that is
105 // different from kUndefinedField, which is used for "null field".
106 static constexpr Field kEndField = Field(0xf, 0xf);
107 
nextPositionImpl(ConstrainedFieldPosition & cfpos,Field numericField,UErrorCode &) const108 bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& cfpos, Field numericField, UErrorCode& /*status*/) const {
109     int32_t fieldStart = -1;
110     Field currField = kUndefinedField;
111     bool prevIsSpan = false;
112     int32_t nextSpanStart = -1;
113     if (spanIndicesCount > 0) {
114         int64_t si = cfpos.getInt64IterationContext();
115         U_ASSERT(si <= spanIndicesCount);
116         if (si < spanIndicesCount) {
117             nextSpanStart = spanIndices[si].start;
118         }
119         if (si > 0) {
120             prevIsSpan = cfpos.getCategory() == spanIndices[si-1].category
121                 && cfpos.getField() == spanIndices[si-1].spanValue;
122         }
123     }
124     bool prevIsNumeric = false;
125     if (numericField != kUndefinedField) {
126         prevIsNumeric = cfpos.getCategory() == numericField.getCategory()
127             && cfpos.getField() == numericField.getField();
128     }
129     bool prevIsInteger = cfpos.getCategory() == UFIELD_CATEGORY_NUMBER
130         && cfpos.getField() == UNUM_INTEGER_FIELD;
131 
132     for (int32_t i = fString.fZero + cfpos.getLimit(); i <= fString.fZero + fString.fLength; i++) {
133         Field _field = (i < fString.fZero + fString.fLength) ? fString.getFieldPtr()[i] : kEndField;
134         // Case 1: currently scanning a field.
135         if (currField != kUndefinedField) {
136             if (currField != _field) {
137                 int32_t end = i - fString.fZero;
138                 // Grouping separators can be whitespace; don't throw them out!
139                 if (isTrimmable(currField)) {
140                     end = trimBack(i - fString.fZero);
141                 }
142                 if (end <= fieldStart) {
143                     // Entire field position is ignorable; skip.
144                     fieldStart = -1;
145                     currField = kUndefinedField;
146                     i--;  // look at this index again
147                     continue;
148                 }
149                 int32_t start = fieldStart;
150                 if (isTrimmable(currField)) {
151                     start = trimFront(start);
152                 }
153                 cfpos.setState(currField.getCategory(), currField.getField(), start, end);
154                 return true;
155             }
156             continue;
157         }
158         // Special case: emit normalField if we are pointing at the end of spanField.
159         if (i > fString.fZero && prevIsSpan) {
160             int64_t si = cfpos.getInt64IterationContext() - 1;
161             U_ASSERT(si >= 0);
162             int32_t previ = i - spanIndices[si].length;
163             U_ASSERT(previ >= fString.fZero);
164             Field prevField = fString.getFieldPtr()[previ];
165             if (prevField == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) {
166                 // Special handling for ULISTFMT_ELEMENT_FIELD
167                 if (cfpos.matchesField(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) {
168                     fieldStart = i - fString.fZero - spanIndices[si].length;
169                     int32_t end = fieldStart + spanIndices[si].length;
170                     cfpos.setState(
171                         UFIELD_CATEGORY_LIST,
172                         ULISTFMT_ELEMENT_FIELD,
173                         fieldStart,
174                         end);
175                     return true;
176                 } else {
177                     prevIsSpan = false;
178                 }
179             } else {
180                 // Re-wind, since there may be multiple fields in the span.
181                 i = previ;
182                 _field = prevField;
183             }
184         }
185         // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER.
186         if (cfpos.matchesField(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)
187                 && i > fString.fZero
188                 && !prevIsInteger
189                 && !prevIsNumeric
190                 && isIntOrGroup(fString.getFieldPtr()[i - 1])
191                 && !isIntOrGroup(_field)) {
192             int j = i - 1;
193             for (; j >= fString.fZero && isIntOrGroup(fString.getFieldPtr()[j]); j--) {}
194             cfpos.setState(
195                 UFIELD_CATEGORY_NUMBER,
196                 UNUM_INTEGER_FIELD,
197                 j - fString.fZero + 1,
198                 i - fString.fZero);
199             return true;
200         }
201         // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC.
202         if (numericField != kUndefinedField
203                 && cfpos.matchesField(numericField.getCategory(), numericField.getField())
204                 && i > fString.fZero
205                 && !prevIsNumeric
206                 && fString.getFieldPtr()[i - 1].isNumeric()
207                 && !_field.isNumeric()) {
208             // Re-wind to the beginning of the field and then emit it
209             int32_t j = i - 1;
210             for (; j >= fString.fZero && fString.getFieldPtr()[j].isNumeric(); j--) {}
211             cfpos.setState(
212                 numericField.getCategory(),
213                 numericField.getField(),
214                 j - fString.fZero + 1,
215                 i - fString.fZero);
216             return true;
217         }
218         // Check for span field
219         if (!prevIsSpan && (
220                 _field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD) ||
221                 i - fString.fZero == nextSpanStart)) {
222             int64_t si = cfpos.getInt64IterationContext();
223             if (si >= spanIndicesCount) {
224                 break;
225             }
226             UFieldCategory spanCategory = spanIndices[si].category;
227             int32_t spanValue = spanIndices[si].spanValue;
228             int32_t length = spanIndices[si].length;
229             cfpos.setInt64IterationContext(si + 1);
230             if (si + 1 < spanIndicesCount) {
231                 nextSpanStart = spanIndices[si + 1].start;
232             }
233             if (cfpos.matchesField(spanCategory, spanValue)) {
234                 fieldStart = i - fString.fZero;
235                 int32_t end = fieldStart + length;
236                 cfpos.setState(
237                     spanCategory,
238                     spanValue,
239                     fieldStart,
240                     end);
241                 return true;
242             } else if (_field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) {
243                 // Special handling for ULISTFMT_ELEMENT_FIELD
244                 if (cfpos.matchesField(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) {
245                     fieldStart = i - fString.fZero;
246                     int32_t end = fieldStart + length;
247                     cfpos.setState(
248                         UFIELD_CATEGORY_LIST,
249                         ULISTFMT_ELEMENT_FIELD,
250                         fieldStart,
251                         end);
252                     return true;
253                 } else {
254                     // Failed to match; jump ahead
255                     i += length - 1;
256                     // goto loopend
257                 }
258             }
259         }
260         // Special case: skip over INTEGER; will be coalesced later.
261         else if (_field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)) {
262             _field = kUndefinedField;
263         }
264         // No field starting at this position.
265         else if (_field.isUndefined() || _field == kEndField) {
266             // goto loopend
267         }
268         // No SpanField
269         else if (cfpos.matchesField(_field.getCategory(), _field.getField())) {
270             fieldStart = i - fString.fZero;
271             currField = _field;
272         }
273         // loopend:
274         prevIsSpan = false;
275         prevIsNumeric = false;
276         prevIsInteger = false;
277     }
278 
279     U_ASSERT(currField == kUndefinedField);
280     // Always set the position to the end so that we don't revisit previous sections
281     cfpos.setState(
282         cfpos.getCategory(),
283         cfpos.getField(),
284         fString.fLength,
285         fString.fLength);
286     return false;
287 }
288 
appendSpanInfo(UFieldCategory category,int32_t spanValue,int32_t start,int32_t length,UErrorCode & status)289 void FormattedValueStringBuilderImpl::appendSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) {
290     if (U_FAILURE(status)) { return; }
291     U_ASSERT(spanIndices.getCapacity() >= spanIndicesCount);
292     if (spanIndices.getCapacity() == spanIndicesCount) {
293         if (!spanIndices.resize(spanIndicesCount * 2, spanIndicesCount)) {
294             status = U_MEMORY_ALLOCATION_ERROR;
295             return;
296         }
297     }
298     spanIndices[spanIndicesCount] = {category, spanValue, start, length};
299     spanIndicesCount++;
300 }
301 
prependSpanInfo(UFieldCategory category,int32_t spanValue,int32_t start,int32_t length,UErrorCode & status)302 void FormattedValueStringBuilderImpl::prependSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) {
303     if (U_FAILURE(status)) { return; }
304     U_ASSERT(spanIndices.getCapacity() >= spanIndicesCount);
305     if (spanIndices.getCapacity() == spanIndicesCount) {
306         if (!spanIndices.resize(spanIndicesCount * 2, spanIndicesCount)) {
307             status = U_MEMORY_ALLOCATION_ERROR;
308             return;
309         }
310     }
311     for (int32_t i = spanIndicesCount - 1; i >= 0; i--) {
312         spanIndices[i+1] = spanIndices[i];
313     }
314     spanIndices[0] = {category, spanValue, start, length};
315     spanIndicesCount++;
316 }
317 
isIntOrGroup(Field field)318 bool FormattedValueStringBuilderImpl::isIntOrGroup(Field field) {
319     return field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)
320         || field == Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD);
321 }
322 
isTrimmable(Field field)323 bool FormattedValueStringBuilderImpl::isTrimmable(Field field) {
324     return field != Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD)
325         && field.getCategory() != UFIELD_CATEGORY_LIST;
326 }
327 
trimBack(int32_t limit) const328 int32_t FormattedValueStringBuilderImpl::trimBack(int32_t limit) const {
329     return unisets::get(unisets::DEFAULT_IGNORABLES)->spanBack(
330         fString.getCharPtr() + fString.fZero,
331         limit,
332         USET_SPAN_CONTAINED);
333 }
334 
trimFront(int32_t start) const335 int32_t FormattedValueStringBuilderImpl::trimFront(int32_t start) const {
336     return start + unisets::get(unisets::DEFAULT_IGNORABLES)->span(
337         fString.getCharPtr() + fString.fZero + start,
338         fString.fLength - start,
339         USET_SPAN_CONTAINED);
340 }
341 
342 
343 U_NAMESPACE_END
344 
345 #endif /* #if !UCONFIG_NO_FORMATTING */
346