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 
19 U_NAMESPACE_BEGIN
20 
21 
22 typedef FormattedStringBuilder::Field Field;
23 
24 
FormattedValueStringBuilderImpl(Field numericField)25 FormattedValueStringBuilderImpl::FormattedValueStringBuilderImpl(Field numericField)
26         : fNumericField(numericField) {
27 }
28 
~FormattedValueStringBuilderImpl()29 FormattedValueStringBuilderImpl::~FormattedValueStringBuilderImpl() {
30 }
31 
32 
toString(UErrorCode &) const33 UnicodeString FormattedValueStringBuilderImpl::toString(UErrorCode&) const {
34     return fString.toUnicodeString();
35 }
36 
toTempString(UErrorCode &) const37 UnicodeString FormattedValueStringBuilderImpl::toTempString(UErrorCode&) const {
38     return fString.toTempUnicodeString();
39 }
40 
appendTo(Appendable & appendable,UErrorCode &) const41 Appendable& FormattedValueStringBuilderImpl::appendTo(Appendable& appendable, UErrorCode&) const {
42     appendable.appendString(fString.chars(), fString.length());
43     return appendable;
44 }
45 
nextPosition(ConstrainedFieldPosition & cfpos,UErrorCode & status) const46 UBool FormattedValueStringBuilderImpl::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const {
47     // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
48     return nextPositionImpl(cfpos, fNumericField, status) ? TRUE : FALSE;
49 }
50 
nextFieldPosition(FieldPosition & fp,UErrorCode & status) const51 UBool FormattedValueStringBuilderImpl::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const {
52     int32_t rawField = fp.getField();
53 
54     if (rawField == FieldPosition::DONT_CARE) {
55         return FALSE;
56     }
57 
58     if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) {
59         status = U_ILLEGAL_ARGUMENT_ERROR;
60         return FALSE;
61     }
62 
63     ConstrainedFieldPosition cfpos;
64     cfpos.constrainField(UFIELD_CATEGORY_NUMBER, rawField);
65     cfpos.setState(UFIELD_CATEGORY_NUMBER, rawField, fp.getBeginIndex(), fp.getEndIndex());
66     if (nextPositionImpl(cfpos, 0, status)) {
67         fp.setBeginIndex(cfpos.getStart());
68         fp.setEndIndex(cfpos.getLimit());
69         return TRUE;
70     }
71 
72     // Special case: fraction should start after integer if fraction is not present
73     if (rawField == UNUM_FRACTION_FIELD && fp.getEndIndex() == 0) {
74         bool inside = false;
75         int32_t i = fString.fZero;
76         for (; i < fString.fZero + fString.fLength; i++) {
77             if (isIntOrGroup(fString.getFieldPtr()[i]) || fString.getFieldPtr()[i] == UNUM_DECIMAL_SEPARATOR_FIELD) {
78                 inside = true;
79             } else if (inside) {
80                 break;
81             }
82         }
83         fp.setBeginIndex(i - fString.fZero);
84         fp.setEndIndex(i - fString.fZero);
85     }
86 
87     return FALSE;
88 }
89 
getAllFieldPositions(FieldPositionIteratorHandler & fpih,UErrorCode & status) const90 void FormattedValueStringBuilderImpl::getAllFieldPositions(FieldPositionIteratorHandler& fpih,
91                                                UErrorCode& status) const {
92     ConstrainedFieldPosition cfpos;
93     while (nextPositionImpl(cfpos, 0, status)) {
94         fpih.addAttribute(cfpos.getField(), cfpos.getStart(), cfpos.getLimit());
95     }
96 }
97 
98 // Signal the end of the string using a field that doesn't exist and that is
99 // different from UNUM_FIELD_COUNT, which is used for "null number field".
100 static constexpr Field kEndField = 0xff;
101 
nextPositionImpl(ConstrainedFieldPosition & cfpos,Field numericField,UErrorCode &) const102 bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& cfpos, Field numericField, UErrorCode& /*status*/) const {
103     auto numericCAF = StringBuilderFieldUtils::expand(numericField);
104     int32_t fieldStart = -1;
105     Field currField = UNUM_FIELD_COUNT;
106     for (int32_t i = fString.fZero + cfpos.getLimit(); i <= fString.fZero + fString.fLength; i++) {
107         Field _field = (i < fString.fZero + fString.fLength) ? fString.getFieldPtr()[i] : kEndField;
108         // Case 1: currently scanning a field.
109         if (currField != UNUM_FIELD_COUNT) {
110             if (currField != _field) {
111                 int32_t end = i - fString.fZero;
112                 // Grouping separators can be whitespace; don't throw them out!
113                 if (currField != UNUM_GROUPING_SEPARATOR_FIELD) {
114                     end = trimBack(i - fString.fZero);
115                 }
116                 if (end <= fieldStart) {
117                     // Entire field position is ignorable; skip.
118                     fieldStart = -1;
119                     currField = UNUM_FIELD_COUNT;
120                     i--;  // look at this index again
121                     continue;
122                 }
123                 int32_t start = fieldStart;
124                 if (currField != UNUM_GROUPING_SEPARATOR_FIELD) {
125                     start = trimFront(start);
126                 }
127                 auto caf = StringBuilderFieldUtils::expand(currField);
128                 cfpos.setState(caf.category, caf.field, start, end);
129                 return true;
130             }
131             continue;
132         }
133         // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER.
134         if (cfpos.matchesField(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)
135                 && i > fString.fZero
136                 // don't return the same field twice in a row:
137                 && i - fString.fZero > cfpos.getLimit()
138                 && isIntOrGroup(fString.getFieldPtr()[i - 1])
139                 && !isIntOrGroup(_field)) {
140             int j = i - 1;
141             for (; j >= fString.fZero && isIntOrGroup(fString.getFieldPtr()[j]); j--) {}
142             cfpos.setState(
143                 UFIELD_CATEGORY_NUMBER,
144                 UNUM_INTEGER_FIELD,
145                 j - fString.fZero + 1,
146                 i - fString.fZero);
147             return true;
148         }
149         // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC.
150         if (numericField != 0
151                 && cfpos.matchesField(numericCAF.category, numericCAF.field)
152                 && i > fString.fZero
153                 // don't return the same field twice in a row:
154                 && (i - fString.fZero > cfpos.getLimit()
155                     || cfpos.getCategory() != numericCAF.category
156                     || cfpos.getField() != numericCAF.field)
157                 && isNumericField(fString.getFieldPtr()[i - 1])
158                 && !isNumericField(_field)) {
159             int j = i - 1;
160             for (; j >= fString.fZero && isNumericField(fString.getFieldPtr()[j]); j--) {}
161             cfpos.setState(
162                 numericCAF.category,
163                 numericCAF.field,
164                 j - fString.fZero + 1,
165                 i - fString.fZero);
166             return true;
167         }
168         // Special case: skip over INTEGER; will be coalesced later.
169         if (_field == UNUM_INTEGER_FIELD) {
170             _field = UNUM_FIELD_COUNT;
171         }
172         // Case 2: no field starting at this position.
173         if (_field == UNUM_FIELD_COUNT || _field == kEndField) {
174             continue;
175         }
176         // Case 3: check for field starting at this position
177         auto caf = StringBuilderFieldUtils::expand(_field);
178         if (cfpos.matchesField(caf.category, caf.field)) {
179             fieldStart = i - fString.fZero;
180             currField = _field;
181         }
182     }
183 
184     U_ASSERT(currField == UNUM_FIELD_COUNT);
185     return false;
186 }
187 
isIntOrGroup(Field field)188 bool FormattedValueStringBuilderImpl::isIntOrGroup(Field field) {
189     return field == UNUM_INTEGER_FIELD
190         || field == UNUM_GROUPING_SEPARATOR_FIELD;
191 }
192 
isNumericField(Field field)193 bool FormattedValueStringBuilderImpl::isNumericField(Field field) {
194     return StringBuilderFieldUtils::isNumericField(field);
195 }
196 
trimBack(int32_t limit) const197 int32_t FormattedValueStringBuilderImpl::trimBack(int32_t limit) const {
198     return unisets::get(unisets::DEFAULT_IGNORABLES)->spanBack(
199         fString.getCharPtr() + fString.fZero,
200         limit,
201         USET_SPAN_CONTAINED);
202 }
203 
trimFront(int32_t start) const204 int32_t FormattedValueStringBuilderImpl::trimFront(int32_t start) const {
205     return start + unisets::get(unisets::DEFAULT_IGNORABLES)->span(
206         fString.getCharPtr() + fString.fZero + start,
207         fString.fLength - start,
208         USET_SPAN_CONTAINED);
209 }
210 
211 
212 U_NAMESPACE_END
213 
214 #endif /* #if !UCONFIG_NO_FORMATTING */
215