formattedval_sbimpl.cpp (13642B)
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 26 FormattedValueStringBuilderImpl::FormattedValueStringBuilderImpl(Field numericField) 27 : fNumericField(numericField) { 28 } 29 30 FormattedValueStringBuilderImpl::~FormattedValueStringBuilderImpl() { 31 } 32 33 34 UnicodeString FormattedValueStringBuilderImpl::toString(UErrorCode&) const { 35 return fString.toUnicodeString(); 36 } 37 38 UnicodeString FormattedValueStringBuilderImpl::toTempString(UErrorCode&) const { 39 return fString.toTempUnicodeString(); 40 } 41 42 Appendable& FormattedValueStringBuilderImpl::appendTo(Appendable& appendable, UErrorCode&) const { 43 appendable.appendString(fString.chars(), fString.length()); 44 return appendable; 45 } 46 47 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 52 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 91 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 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 108 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 (length == 0) { 234 // ICU-21871: Don't return fields on empty spans 235 i--; 236 continue; 237 } 238 if (cfpos.matchesField(spanCategory, spanValue)) { 239 fieldStart = i - fString.fZero; 240 int32_t end = fieldStart + length; 241 cfpos.setState( 242 spanCategory, 243 spanValue, 244 fieldStart, 245 end); 246 return true; 247 } else if (_field == Field(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { 248 // Special handling for ULISTFMT_ELEMENT_FIELD 249 if (cfpos.matchesField(UFIELD_CATEGORY_LIST, ULISTFMT_ELEMENT_FIELD)) { 250 fieldStart = i - fString.fZero; 251 int32_t end = fieldStart + length; 252 cfpos.setState( 253 UFIELD_CATEGORY_LIST, 254 ULISTFMT_ELEMENT_FIELD, 255 fieldStart, 256 end); 257 return true; 258 } else { 259 // Failed to match; jump ahead 260 i += length - 1; 261 // goto loopend 262 } 263 } 264 } 265 // Special case: skip over INTEGER; will be coalesced later. 266 else if (_field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)) { 267 _field = kUndefinedField; 268 } 269 // No field starting at this position. 270 else if (_field.isUndefined() || _field == kEndField) { 271 // goto loopend 272 } 273 // No SpanField 274 else if (cfpos.matchesField(_field.getCategory(), _field.getField())) { 275 fieldStart = i - fString.fZero; 276 currField = _field; 277 } 278 // loopend: 279 prevIsSpan = false; 280 prevIsNumeric = false; 281 prevIsInteger = false; 282 } 283 284 U_ASSERT(currField == kUndefinedField); 285 // Always set the position to the end so that we don't revisit previous sections 286 cfpos.setState( 287 cfpos.getCategory(), 288 cfpos.getField(), 289 fString.fLength, 290 fString.fLength); 291 return false; 292 } 293 294 void FormattedValueStringBuilderImpl::appendSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) { 295 if (U_FAILURE(status)) { return; } 296 U_ASSERT(spanIndices.getCapacity() >= spanIndicesCount); 297 if (spanIndices.getCapacity() == spanIndicesCount) { 298 if (!spanIndices.resize(spanIndicesCount * 2, spanIndicesCount)) { 299 status = U_MEMORY_ALLOCATION_ERROR; 300 return; 301 } 302 } 303 spanIndices[spanIndicesCount] = {category, spanValue, start, length}; 304 spanIndicesCount++; 305 } 306 307 void FormattedValueStringBuilderImpl::prependSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) { 308 if (U_FAILURE(status)) { return; } 309 U_ASSERT(spanIndices.getCapacity() >= spanIndicesCount); 310 if (spanIndices.getCapacity() == spanIndicesCount) { 311 if (!spanIndices.resize(spanIndicesCount * 2, spanIndicesCount)) { 312 status = U_MEMORY_ALLOCATION_ERROR; 313 return; 314 } 315 } 316 for (int32_t i = spanIndicesCount - 1; i >= 0; i--) { 317 spanIndices[i+1] = spanIndices[i]; 318 } 319 spanIndices[0] = {category, spanValue, start, length}; 320 spanIndicesCount++; 321 } 322 323 bool FormattedValueStringBuilderImpl::isIntOrGroup(Field field) { 324 return field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD) 325 || field == Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD); 326 } 327 328 bool FormattedValueStringBuilderImpl::isTrimmable(Field field) { 329 return field != Field(UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD) 330 && field.getCategory() != UFIELD_CATEGORY_LIST; 331 } 332 333 int32_t FormattedValueStringBuilderImpl::trimBack(int32_t limit) const { 334 return unisets::get(unisets::DEFAULT_IGNORABLES)->spanBack( 335 fString.getCharPtr() + fString.fZero, 336 limit, 337 USET_SPAN_CONTAINED); 338 } 339 340 int32_t FormattedValueStringBuilderImpl::trimFront(int32_t start) const { 341 return start + unisets::get(unisets::DEFAULT_IGNORABLES)->span( 342 fString.getCharPtr() + fString.fZero + start, 343 fString.fLength - start, 344 USET_SPAN_CONTAINED); 345 } 346 347 348 U_NAMESPACE_END 349 350 #endif /* #if !UCONFIG_NO_FORMATTING */