• 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 
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, kUndefinedField, 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] == Field(UFIELD_CATEGORY_NUMBER, 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, kUndefinedField, 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 kUndefinedField, which is used for "null field".
100 static constexpr Field kEndField = Field(0xf, 0xf);
101 
nextPositionImpl(ConstrainedFieldPosition & cfpos,Field numericField,UErrorCode &) const102 bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& cfpos, Field numericField, UErrorCode& /*status*/) const {
103     int32_t fieldStart = -1;
104     Field currField = kUndefinedField;
105     for (int32_t i = fString.fZero + cfpos.getLimit(); i <= fString.fZero + fString.fLength; i++) {
106         Field _field = (i < fString.fZero + fString.fLength) ? fString.getFieldPtr()[i] : kEndField;
107         // Case 1: currently scanning a field.
108         if (currField != kUndefinedField) {
109             if (currField != _field) {
110                 int32_t end = i - fString.fZero;
111                 // Grouping separators can be whitespace; don't throw them out!
112                 if (currField != Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD)) {
113                     end = trimBack(i - fString.fZero);
114                 }
115                 if (end <= fieldStart) {
116                     // Entire field position is ignorable; skip.
117                     fieldStart = -1;
118                     currField = kUndefinedField;
119                     i--;  // look at this index again
120                     continue;
121                 }
122                 int32_t start = fieldStart;
123                 if (currField != Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD)) {
124                     start = trimFront(start);
125                 }
126                 cfpos.setState(currField.getCategory(), currField.getField(), start, end);
127                 return true;
128             }
129             continue;
130         }
131         // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER.
132         if (cfpos.matchesField(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)
133                 && i > fString.fZero
134                 // don't return the same field twice in a row:
135                 && i - fString.fZero > cfpos.getLimit()
136                 && isIntOrGroup(fString.getFieldPtr()[i - 1])
137                 && !isIntOrGroup(_field)) {
138             int j = i - 1;
139             for (; j >= fString.fZero && isIntOrGroup(fString.getFieldPtr()[j]); j--) {}
140             cfpos.setState(
141                 UFIELD_CATEGORY_NUMBER,
142                 UNUM_INTEGER_FIELD,
143                 j - fString.fZero + 1,
144                 i - fString.fZero);
145             return true;
146         }
147         // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC.
148         if (numericField != kUndefinedField
149                 && cfpos.matchesField(numericField.getCategory(), numericField.getField())
150                 && i > fString.fZero
151                 // don't return the same field twice in a row:
152                 && (i - fString.fZero > cfpos.getLimit()
153                     || cfpos.getCategory() != numericField.getCategory()
154                     || cfpos.getField() != numericField.getField())
155                 && fString.getFieldPtr()[i - 1].isNumeric()
156                 && !_field.isNumeric()) {
157             int j = i - 1;
158             for (; j >= fString.fZero && fString.getFieldPtr()[j].isNumeric(); j--) {}
159             cfpos.setState(
160                 numericField.getCategory(),
161                 numericField.getField(),
162                 j - fString.fZero + 1,
163                 i - fString.fZero);
164             return true;
165         }
166         // Special case: skip over INTEGER; will be coalesced later.
167         if (_field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)) {
168             _field = kUndefinedField;
169         }
170         // Case 2: no field starting at this position.
171         if (_field.isUndefined() || _field == kEndField) {
172             continue;
173         }
174         // Case 3: check for field starting at this position
175         if (cfpos.matchesField(_field.getCategory(), _field.getField())) {
176             fieldStart = i - fString.fZero;
177             currField = _field;
178         }
179     }
180 
181     U_ASSERT(currField == kUndefinedField);
182     return false;
183 }
184 
isIntOrGroup(Field field)185 bool FormattedValueStringBuilderImpl::isIntOrGroup(Field field) {
186     return field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)
187         || field == Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD);
188 }
189 
trimBack(int32_t limit) const190 int32_t FormattedValueStringBuilderImpl::trimBack(int32_t limit) const {
191     return unisets::get(unisets::DEFAULT_IGNORABLES)->spanBack(
192         fString.getCharPtr() + fString.fZero,
193         limit,
194         USET_SPAN_CONTAINED);
195 }
196 
trimFront(int32_t start) const197 int32_t FormattedValueStringBuilderImpl::trimFront(int32_t start) const {
198     return start + unisets::get(unisets::DEFAULT_IGNORABLES)->span(
199         fString.getCharPtr() + fString.fZero + start,
200         fString.fLength - start,
201         USET_SPAN_CONTAINED);
202 }
203 
204 
205 U_NAMESPACE_END
206 
207 #endif /* #if !UCONFIG_NO_FORMATTING */
208