From 6daab85db451455a1e2d7427301010f8ce6d2624 Mon Sep 17 00:00:00 2001 From: Shane Carr Date: Fri, 5 Apr 2019 16:43:19 -0700 Subject: [PATCH] ICU-20429 Renaming [Number->Formatted]StringBuilder and refactoring. - StringSegment, ICU4C: * Moved to top icu namespace * Compilation unit renamed to string_segment. - NumberStringBuilder, C and J: * Moved to main icu namespace * Compilation unit renamed to formatted_string_builder * Renamed class to FormattedStringBuilder - Moves nextPosition logic of NumberStringBuilder to helper class --- icu4c/source/i18n/Makefile.in | 7 +- ...ilder.cpp => formatted_string_builder.cpp} | 226 +++-------------- ...ngbuilder.h => formatted_string_builder.h} | 103 +++++--- icu4c/source/i18n/formattedval_impl.h | 39 ++- icu4c/source/i18n/formattedval_sbimpl.cpp | 182 +++++++++++++- icu4c/source/i18n/i18n.vcxproj | 8 +- icu4c/source/i18n/i18n.vcxproj.filters | 8 +- icu4c/source/i18n/i18n_uwp.vcxproj | 8 +- icu4c/source/i18n/number_affixutils.cpp | 2 +- icu4c/source/i18n/number_affixutils.h | 10 +- icu4c/source/i18n/number_asformat.cpp | 8 +- icu4c/source/i18n/number_fluent.cpp | 2 +- icu4c/source/i18n/number_formatimpl.cpp | 18 +- icu4c/source/i18n/number_formatimpl.h | 20 +- icu4c/source/i18n/number_modifiers.cpp | 20 +- icu4c/source/i18n/number_modifiers.h | 46 ++-- icu4c/source/i18n/number_output.cpp | 5 +- icu4c/source/i18n/number_padding.cpp | 6 +- icu4c/source/i18n/number_patternmodifier.cpp | 10 +- icu4c/source/i18n/number_patternmodifier.h | 10 +- icu4c/source/i18n/number_scientific.cpp | 4 +- icu4c/source/i18n/number_scientific.h | 2 +- icu4c/source/i18n/number_skeletons.cpp | 1 + icu4c/source/i18n/number_skeletons.h | 6 +- icu4c/source/i18n/number_types.h | 16 +- icu4c/source/i18n/number_utils.h | 45 +--- icu4c/source/i18n/number_utypes.h | 6 +- icu4c/source/i18n/numparse_affixes.cpp | 1 + icu4c/source/i18n/numparse_compositions.cpp | 1 + icu4c/source/i18n/numparse_currency.cpp | 1 + icu4c/source/i18n/numparse_decimal.cpp | 1 + icu4c/source/i18n/numparse_impl.h | 1 + icu4c/source/i18n/numparse_parsednumber.cpp | 1 + icu4c/source/i18n/numparse_scientific.cpp | 1 + icu4c/source/i18n/numparse_stringsegment.h | 24 -- icu4c/source/i18n/numparse_symbols.cpp | 1 + icu4c/source/i18n/numparse_types.h | 114 +-------- icu4c/source/i18n/numrange_fluent.cpp | 4 +- icu4c/source/i18n/numrange_impl.cpp | 2 +- icu4c/source/i18n/numrange_impl.h | 6 +- icu4c/source/i18n/quantityformatter.cpp | 4 +- icu4c/source/i18n/quantityformatter.h | 11 +- icu4c/source/i18n/reldatefmt.cpp | 10 +- ...e_stringsegment.cpp => string_segment.cpp} | 7 +- icu4c/source/i18n/string_segment.h | 134 ++++++++++ icu4c/source/i18n/unicode/numberformatter.h | 4 +- icu4c/source/test/depstest/dependencies.txt | 4 +- icu4c/source/test/intltest/Makefile.in | 4 +- ....cpp => formatted_string_builder_test.cpp} | 87 ++++--- icu4c/source/test/intltest/intltest.vcxproj | 4 +- .../test/intltest/intltest.vcxproj.filters | 8 +- icu4c/source/test/intltest/itformat.cpp | 20 ++ icu4c/source/test/intltest/numbertest.h | 43 +--- .../test/intltest/numbertest_affixutils.cpp | 4 +- .../test/intltest/numbertest_modifiers.cpp | 24 +- .../intltest/numbertest_patternmodifier.cpp | 12 +- ...ingsegment.cpp => string_segment_test.cpp} | 20 +- ...ilder.java => FormattedStringBuilder.java} | 231 +++--------------- .../impl/FormattedValueStringBuilderImpl.java | 193 +++++++++++++++ .../src/com/ibm/icu/impl/StringSegment.java | 16 +- .../com/ibm/icu/impl/number/AffixUtils.java | 9 +- .../impl/number/ConstantAffixModifier.java | 4 +- .../number/ConstantMultiFieldModifier.java | 18 +- .../CurrencySpacingEnabledModifier.java | 11 +- .../src/com/ibm/icu/impl/number/Modifier.java | 3 +- .../impl/number/MutablePatternModifier.java | 19 +- .../src/com/ibm/icu/impl/number/Padder.java | 6 +- .../ibm/icu/impl/number/SimpleModifier.java | 11 +- .../com/ibm/icu/number/FormattedNumber.java | 17 +- .../ibm/icu/number/FormattedNumberRange.java | 17 +- .../icu/number/LocalizedNumberFormatter.java | 10 +- .../ibm/icu/number/NumberFormatterImpl.java | 20 +- .../icu/number/NumberRangeFormatterImpl.java | 10 +- .../ibm/icu/number/ScientificNotation.java | 8 +- .../icu/text/RelativeDateTimeFormatter.java | 31 +-- .../FormattedStringBuilderTest.java} | 43 ++-- .../icu/dev/test/number/AffixUtilsTest.java | 6 +- .../dev/test/number/DecimalQuantityTest.java | 6 +- .../ibm/icu/dev/test/number/ModifierTest.java | 24 +- .../number/MutablePatternModifierTest.java | 14 +- 80 files changed, 1113 insertions(+), 990 deletions(-) rename icu4c/source/i18n/{number_stringbuilder.cpp => formatted_string_builder.cpp} (58%) rename icu4c/source/i18n/{number_stringbuilder.h => formatted_string_builder.h} (54%) delete mode 100644 icu4c/source/i18n/numparse_stringsegment.h rename icu4c/source/i18n/{numparse_stringsegment.cpp => string_segment.cpp} (96%) create mode 100644 icu4c/source/i18n/string_segment.h rename icu4c/source/test/intltest/{numbertest_stringbuilder.cpp => formatted_string_builder_test.cpp} (81%) rename icu4c/source/test/intltest/{numbertest_stringsegment.cpp => string_segment_test.cpp} (91%) rename icu4j/main/classes/core/src/com/ibm/icu/impl/{number/NumberStringBuilder.java => FormattedStringBuilder.java} (64%) create mode 100644 icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedValueStringBuilderImpl.java rename icu4j/main/tests/core/src/com/ibm/icu/dev/test/{number/NumberStringBuilderTest.java => format/FormattedStringBuilderTest.java} (87%) diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in index 2d001879e8f..7487d30354f 100644 --- a/icu4c/source/i18n/Makefile.in +++ b/icu4c/source/i18n/Makefile.in @@ -103,16 +103,17 @@ number_affixutils.o number_compact.o number_decimalquantity.o \ number_decimfmtprops.o number_fluent.o number_formatimpl.o number_grouping.o \ number_integerwidth.o number_longnames.o number_modifiers.o number_notation.o number_output.o \ number_padding.o number_patternmodifier.o number_patternstring.o \ -number_rounding.o number_scientific.o number_stringbuilder.o number_utils.o number_asformat.o \ +number_rounding.o number_scientific.o number_utils.o number_asformat.o \ number_mapper.o number_multiplier.o number_currencysymbols.o number_skeletons.o number_capi.o \ double-conversion.o double-conversion-bignum-dtoa.o double-conversion-bignum.o \ double-conversion-cached-powers.o double-conversion-diy-fp.o \ double-conversion-fast-dtoa.o double-conversion-strtod.o \ -numparse_stringsegment.o numparse_parsednumber.o numparse_impl.o \ +string_segment.o numparse_parsednumber.o numparse_impl.o \ numparse_symbols.o numparse_decimal.o numparse_scientific.o numparse_currency.o \ numparse_affixes.o numparse_compositions.o numparse_validators.o \ numrange_fluent.o numrange_impl.o \ -erarules.o formattedvalue.o formattedval_iterimpl.o formattedval_sbimpl.o +erarules.o \ +formattedvalue.o formattedval_iterimpl.o formattedval_sbimpl.o formatted_string_builder.o ## Header files to install HEADERS = $(srcdir)/unicode/*.h diff --git a/icu4c/source/i18n/number_stringbuilder.cpp b/icu4c/source/i18n/formatted_string_builder.cpp similarity index 58% rename from icu4c/source/i18n/number_stringbuilder.cpp rename to icu4c/source/i18n/formatted_string_builder.cpp index 03300b33ac5..4ffc4c704a1 100644 --- a/icu4c/source/i18n/number_stringbuilder.cpp +++ b/icu4c/source/i18n/formatted_string_builder.cpp @@ -5,14 +5,9 @@ #if !UCONFIG_NO_FORMATTING -#include "number_stringbuilder.h" -#include "static_unicode_sets.h" +#include "formatted_string_builder.h" +#include "unicode/ustring.h" #include "unicode/utf16.h" -#include "number_utils.h" - -using namespace icu; -using namespace icu::number; -using namespace icu::number::impl; namespace { @@ -34,7 +29,10 @@ inline void uprv_memmove2(void* dest, const void* src, size_t len) { } // namespace -NumberStringBuilder::NumberStringBuilder() { + +U_NAMESPACE_BEGIN + +FormattedStringBuilder::FormattedStringBuilder() { #if U_DEBUG // Initializing the memory to non-zero helps catch some bugs that involve // reading from an improperly terminated string. @@ -44,18 +42,18 @@ NumberStringBuilder::NumberStringBuilder() { #endif } -NumberStringBuilder::~NumberStringBuilder() { +FormattedStringBuilder::~FormattedStringBuilder() { if (fUsingHeap) { uprv_free(fChars.heap.ptr); uprv_free(fFields.heap.ptr); } } -NumberStringBuilder::NumberStringBuilder(const NumberStringBuilder &other) { +FormattedStringBuilder::FormattedStringBuilder(const FormattedStringBuilder &other) { *this = other; } -NumberStringBuilder &NumberStringBuilder::operator=(const NumberStringBuilder &other) { +FormattedStringBuilder &FormattedStringBuilder::operator=(const FormattedStringBuilder &other) { // Check for self-assignment if (this == &other) { return *this; @@ -78,7 +76,7 @@ NumberStringBuilder &NumberStringBuilder::operator=(const NumberStringBuilder &o // UErrorCode is not available; fail silently. uprv_free(newChars); uprv_free(newFields); - *this = NumberStringBuilder(); // can't fail + *this = FormattedStringBuilder(); // can't fail return *this; } @@ -97,15 +95,15 @@ NumberStringBuilder &NumberStringBuilder::operator=(const NumberStringBuilder &o return *this; } -int32_t NumberStringBuilder::length() const { +int32_t FormattedStringBuilder::length() const { return fLength; } -int32_t NumberStringBuilder::codePointCount() const { +int32_t FormattedStringBuilder::codePointCount() const { return u_countChar32(getCharPtr() + fZero, fLength); } -UChar32 NumberStringBuilder::getFirstCodePoint() const { +UChar32 FormattedStringBuilder::getFirstCodePoint() const { if (fLength == 0) { return -1; } @@ -114,7 +112,7 @@ UChar32 NumberStringBuilder::getFirstCodePoint() const { return cp; } -UChar32 NumberStringBuilder::getLastCodePoint() const { +UChar32 FormattedStringBuilder::getLastCodePoint() const { if (fLength == 0) { return -1; } @@ -125,13 +123,13 @@ UChar32 NumberStringBuilder::getLastCodePoint() const { return cp; } -UChar32 NumberStringBuilder::codePointAt(int32_t index) const { +UChar32 FormattedStringBuilder::codePointAt(int32_t index) const { UChar32 cp; U16_GET(getCharPtr() + fZero, 0, index, fLength, cp); return cp; } -UChar32 NumberStringBuilder::codePointBefore(int32_t index) const { +UChar32 FormattedStringBuilder::codePointBefore(int32_t index) const { int32_t offset = index; U16_BACK_1(getCharPtr() + fZero, 0, offset); UChar32 cp; @@ -139,19 +137,19 @@ UChar32 NumberStringBuilder::codePointBefore(int32_t index) const { return cp; } -NumberStringBuilder &NumberStringBuilder::clear() { +FormattedStringBuilder &FormattedStringBuilder::clear() { // TODO: Reset the heap here? fZero = getCapacity() / 2; fLength = 0; return *this; } -int32_t NumberStringBuilder::appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status) { +int32_t FormattedStringBuilder::appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status) { return insertCodePoint(fLength, codePoint, field, status); } int32_t -NumberStringBuilder::insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status) { +FormattedStringBuilder::insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status) { int32_t count = U16_LENGTH(codePoint); int32_t position = prepareForInsert(index, count, status); if (U_FAILURE(status)) { @@ -168,11 +166,11 @@ NumberStringBuilder::insertCodePoint(int32_t index, UChar32 codePoint, Field fie return count; } -int32_t NumberStringBuilder::append(const UnicodeString &unistr, Field field, UErrorCode &status) { +int32_t FormattedStringBuilder::append(const UnicodeString &unistr, Field field, UErrorCode &status) { return insert(fLength, unistr, field, status); } -int32_t NumberStringBuilder::insert(int32_t index, const UnicodeString &unistr, Field field, +int32_t FormattedStringBuilder::insert(int32_t index, const UnicodeString &unistr, Field field, UErrorCode &status) { if (unistr.length() == 0) { // Nothing to insert. @@ -186,7 +184,7 @@ int32_t NumberStringBuilder::insert(int32_t index, const UnicodeString &unistr, } int32_t -NumberStringBuilder::insert(int32_t index, const UnicodeString &unistr, int32_t start, int32_t end, +FormattedStringBuilder::insert(int32_t index, const UnicodeString &unistr, int32_t start, int32_t end, Field field, UErrorCode &status) { int32_t count = end - start; int32_t position = prepareForInsert(index, count, status); @@ -201,7 +199,7 @@ NumberStringBuilder::insert(int32_t index, const UnicodeString &unistr, int32_t } int32_t -NumberStringBuilder::splice(int32_t startThis, int32_t endThis, const UnicodeString &unistr, +FormattedStringBuilder::splice(int32_t startThis, int32_t endThis, const UnicodeString &unistr, int32_t startOther, int32_t endOther, Field field, UErrorCode& status) { int32_t thisLength = endThis - startThis; int32_t otherLength = endOther - startOther; @@ -224,12 +222,12 @@ NumberStringBuilder::splice(int32_t startThis, int32_t endThis, const UnicodeSt return count; } -int32_t NumberStringBuilder::append(const NumberStringBuilder &other, UErrorCode &status) { +int32_t FormattedStringBuilder::append(const FormattedStringBuilder &other, UErrorCode &status) { return insert(fLength, other, status); } int32_t -NumberStringBuilder::insert(int32_t index, const NumberStringBuilder &other, UErrorCode &status) { +FormattedStringBuilder::insert(int32_t index, const FormattedStringBuilder &other, UErrorCode &status) { if (this == &other) { status = U_ILLEGAL_ARGUMENT_ERROR; return 0; @@ -250,7 +248,7 @@ NumberStringBuilder::insert(int32_t index, const NumberStringBuilder &other, UEr return count; } -void NumberStringBuilder::writeTerminator(UErrorCode& status) { +void FormattedStringBuilder::writeTerminator(UErrorCode& status) { int32_t position = prepareForInsert(fLength, 1, status); if (U_FAILURE(status)) { return; @@ -260,7 +258,7 @@ void NumberStringBuilder::writeTerminator(UErrorCode& status) { fLength--; } -int32_t NumberStringBuilder::prepareForInsert(int32_t index, int32_t count, UErrorCode &status) { +int32_t FormattedStringBuilder::prepareForInsert(int32_t index, int32_t count, UErrorCode &status) { U_ASSERT(index >= 0); U_ASSERT(index <= fLength); U_ASSERT(count >= 0); @@ -279,7 +277,7 @@ int32_t NumberStringBuilder::prepareForInsert(int32_t index, int32_t count, UErr } } -int32_t NumberStringBuilder::prepareForInsertHelper(int32_t index, int32_t count, UErrorCode &status) { +int32_t FormattedStringBuilder::prepareForInsertHelper(int32_t index, int32_t count, UErrorCode &status) { int32_t oldCapacity = getCapacity(); int32_t oldZero = fZero; char16_t *oldChars = getCharPtr(); @@ -342,7 +340,7 @@ int32_t NumberStringBuilder::prepareForInsertHelper(int32_t index, int32_t count return fZero + index; } -int32_t NumberStringBuilder::remove(int32_t index, int32_t count) { +int32_t FormattedStringBuilder::remove(int32_t index, int32_t count) { // TODO: Reset the heap here? (If the string after removal can fit on stack?) int32_t position = index + fZero; uprv_memmove2(getCharPtr() + position, @@ -355,18 +353,18 @@ int32_t NumberStringBuilder::remove(int32_t index, int32_t count) { return position; } -UnicodeString NumberStringBuilder::toUnicodeString() const { +UnicodeString FormattedStringBuilder::toUnicodeString() const { return UnicodeString(getCharPtr() + fZero, fLength); } -const UnicodeString NumberStringBuilder::toTempUnicodeString() const { +const UnicodeString FormattedStringBuilder::toTempUnicodeString() const { // Readonly-alias constructor: return UnicodeString(FALSE, getCharPtr() + fZero, fLength); } -UnicodeString NumberStringBuilder::toDebugString() const { +UnicodeString FormattedStringBuilder::toDebugString() const { UnicodeString sb; - sb.append(u"= UNUM_FIELD_COUNT) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return FALSE; - } - - ConstrainedFieldPosition cfpos; - cfpos.constrainField(UFIELD_CATEGORY_NUMBER, rawField); - cfpos.setState(UFIELD_CATEGORY_NUMBER, rawField, fp.getBeginIndex(), fp.getEndIndex()); - if (nextPosition(cfpos, 0, status)) { - fp.setBeginIndex(cfpos.getStart()); - fp.setEndIndex(cfpos.getLimit()); - return true; - } - - // Special case: fraction should start after integer if fraction is not present - if (rawField == UNUM_FRACTION_FIELD && fp.getEndIndex() == 0) { - bool inside = false; - int32_t i = fZero; - for (; i < fZero + fLength; i++) { - if (isIntOrGroup(getFieldPtr()[i]) || getFieldPtr()[i] == UNUM_DECIMAL_SEPARATOR_FIELD) { - inside = true; - } else if (inside) { - break; - } - } - fp.setBeginIndex(i - fZero); - fp.setEndIndex(i - fZero); - } - - return false; -} - -void NumberStringBuilder::getAllFieldPositions(FieldPositionIteratorHandler& fpih, - UErrorCode& status) const { - ConstrainedFieldPosition cfpos; - while (nextPosition(cfpos, 0, status)) { - fpih.addAttribute(cfpos.getField(), cfpos.getStart(), cfpos.getLimit()); - } -} - -// Signal the end of the string using a field that doesn't exist and that is -// different from UNUM_FIELD_COUNT, which is used for "null number field". -static constexpr Field kEndField = 0xff; - -bool NumberStringBuilder::nextPosition(ConstrainedFieldPosition& cfpos, Field numericField, UErrorCode& /*status*/) const { - auto numericCAF = NumFieldUtils::expand(numericField); - int32_t fieldStart = -1; - Field currField = UNUM_FIELD_COUNT; - for (int32_t i = fZero + cfpos.getLimit(); i <= fZero + fLength; i++) { - Field _field = (i < fZero + fLength) ? getFieldPtr()[i] : kEndField; - // Case 1: currently scanning a field. - if (currField != UNUM_FIELD_COUNT) { - if (currField != _field) { - int32_t end = i - fZero; - // Grouping separators can be whitespace; don't throw them out! - if (currField != UNUM_GROUPING_SEPARATOR_FIELD) { - end = trimBack(i - fZero); - } - if (end <= fieldStart) { - // Entire field position is ignorable; skip. - fieldStart = -1; - currField = UNUM_FIELD_COUNT; - i--; // look at this index again - continue; - } - int32_t start = fieldStart; - if (currField != UNUM_GROUPING_SEPARATOR_FIELD) { - start = trimFront(start); - } - auto caf = NumFieldUtils::expand(currField); - cfpos.setState(caf.category, caf.field, start, end); - return true; - } - continue; - } - // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER. - if (cfpos.matchesField(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD) - && i > fZero - // don't return the same field twice in a row: - && i - fZero > cfpos.getLimit() - && isIntOrGroup(getFieldPtr()[i - 1]) - && !isIntOrGroup(_field)) { - int j = i - 1; - for (; j >= fZero && isIntOrGroup(getFieldPtr()[j]); j--) {} - cfpos.setState(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD, j - fZero + 1, i - fZero); - return true; - } - // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC. - if (numericField != 0 - && cfpos.matchesField(numericCAF.category, numericCAF.field) - && i > fZero - // don't return the same field twice in a row: - && (i - fZero > cfpos.getLimit() - || cfpos.getCategory() != numericCAF.category - || cfpos.getField() != numericCAF.field) - && isNumericField(getFieldPtr()[i - 1]) - && !isNumericField(_field)) { - int j = i - 1; - for (; j >= fZero && isNumericField(getFieldPtr()[j]); j--) {} - cfpos.setState(numericCAF.category, numericCAF.field, j - fZero + 1, i - fZero); - return true; - } - // Special case: skip over INTEGER; will be coalesced later. - if (_field == UNUM_INTEGER_FIELD) { - _field = UNUM_FIELD_COUNT; - } - // Case 2: no field starting at this position. - if (_field == UNUM_FIELD_COUNT || _field == kEndField) { - continue; - } - // Case 3: check for field starting at this position - auto caf = NumFieldUtils::expand(_field); - if (cfpos.matchesField(caf.category, caf.field)) { - fieldStart = i - fZero; - currField = _field; - } - } - - U_ASSERT(currField == UNUM_FIELD_COUNT); - return false; -} - -bool NumberStringBuilder::containsField(Field field) const { +bool FormattedStringBuilder::containsField(Field field) const { for (int32_t i = 0; i < fLength; i++) { if (field == fieldAt(i)) { return true; @@ -573,27 +442,6 @@ bool NumberStringBuilder::containsField(Field field) const { return false; } -bool NumberStringBuilder::isIntOrGroup(Field field) { - return field == UNUM_INTEGER_FIELD - || field == UNUM_GROUPING_SEPARATOR_FIELD; -} - -bool NumberStringBuilder::isNumericField(Field field) { - return NumFieldUtils::isNumericField(field); -} - -int32_t NumberStringBuilder::trimBack(int32_t limit) const { - return unisets::get(unisets::DEFAULT_IGNORABLES)->spanBack( - getCharPtr() + fZero, - limit, - USET_SPAN_CONTAINED); -} - -int32_t NumberStringBuilder::trimFront(int32_t start) const { - return start + unisets::get(unisets::DEFAULT_IGNORABLES)->span( - getCharPtr() + fZero + start, - fLength - start, - USET_SPAN_CONTAINED); -} +U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/number_stringbuilder.h b/icu4c/source/i18n/formatted_string_builder.h similarity index 54% rename from icu4c/source/i18n/number_stringbuilder.h rename to icu4c/source/i18n/formatted_string_builder.h index d48f6e106cf..cc13b66353d 100644 --- a/icu4c/source/i18n/number_stringbuilder.h +++ b/icu4c/source/i18n/formatted_string_builder.h @@ -9,17 +9,29 @@ #include -#include "unicode/numfmt.h" -#include "unicode/ustring.h" +#include "unicode/unum.h" // for UNUM_FIELD_COUNT #include "cstring.h" #include "uassert.h" -#include "number_types.h" #include "fphdlimp.h" -U_NAMESPACE_BEGIN namespace number { -namespace impl { - -class U_I18N_API NumberStringBuilder : public UMemory { +U_NAMESPACE_BEGIN + +class FormattedValueStringBuilderImpl; + +/** + * A StringBuilder optimized for formatting. It implements the following key + * features beyond a UnicodeString: + * + *
    + *
  1. Efficient prepend as well as append. + *
  2. Keeps tracks of Fields in an efficient manner. + *
+ * + * See also FormattedValueStringBuilderImpl. + * + * @author sffc (Shane Carr) + */ +class U_I18N_API FormattedStringBuilder : public UMemory { private: static const int32_t DEFAULT_CAPACITY = 40; @@ -33,13 +45,19 @@ class U_I18N_API NumberStringBuilder : public UMemory { }; public: - NumberStringBuilder(); + FormattedStringBuilder(); + + ~FormattedStringBuilder(); - ~NumberStringBuilder(); + FormattedStringBuilder(const FormattedStringBuilder &other); - NumberStringBuilder(const NumberStringBuilder &other); + // Convention: bottom 4 bits for field, top 4 bits for field category. + // Field category 0 implies the number category so that the number field + // literals can be directly passed as a Field type. + // See the helper functions in "StringBuilderFieldUtils" below. + typedef uint8_t Field; - NumberStringBuilder &operator=(const NumberStringBuilder &other); + FormattedStringBuilder &operator=(const FormattedStringBuilder &other); int32_t length() const; @@ -65,7 +83,7 @@ class U_I18N_API NumberStringBuilder : public UMemory { UChar32 codePointBefore(int32_t index) const; - NumberStringBuilder &clear(); + FormattedStringBuilder &clear(); int32_t appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status); @@ -81,19 +99,19 @@ class U_I18N_API NumberStringBuilder : public UMemory { int32_t splice(int32_t startThis, int32_t endThis, const UnicodeString &unistr, int32_t startOther, int32_t endOther, Field field, UErrorCode& status); - int32_t append(const NumberStringBuilder &other, UErrorCode &status); + int32_t append(const FormattedStringBuilder &other, UErrorCode &status); - int32_t insert(int32_t index, const NumberStringBuilder &other, UErrorCode &status); + int32_t insert(int32_t index, const FormattedStringBuilder &other, UErrorCode &status); void writeTerminator(UErrorCode& status); /** - * Gets a "safe" UnicodeString that can be used even after the NumberStringBuilder is destructed. + * Gets a "safe" UnicodeString that can be used even after the FormattedStringBuilder is destructed. * */ UnicodeString toUnicodeString() const; /** - * Gets an "unsafe" UnicodeString that is valid only as long as the NumberStringBuilder is alive and + * Gets an "unsafe" UnicodeString that is valid only as long as the FormattedStringBuilder is alive and * unchanged. Slightly faster than toUnicodeString(). */ const UnicodeString toTempUnicodeString() const; @@ -102,13 +120,7 @@ class U_I18N_API NumberStringBuilder : public UMemory { const char16_t *chars() const; - bool contentEquals(const NumberStringBuilder &other) const; - - bool nextFieldPosition(FieldPosition& fp, UErrorCode& status) const; - - void getAllFieldPositions(FieldPositionIteratorHandler& fpih, UErrorCode& status) const; - - bool nextPosition(ConstrainedFieldPosition& cfpos, Field numericField, UErrorCode& status) const; + bool contentEquals(const FormattedStringBuilder &other) const; bool containsField(Field field) const; @@ -145,17 +157,50 @@ class U_I18N_API NumberStringBuilder : public UMemory { int32_t remove(int32_t index, int32_t count); - static bool isIntOrGroup(Field field); + friend class FormattedValueStringBuilderImpl; +}; - static bool isNumericField(Field field); +/** + * Helper functions for dealing with the Field typedef, which stores fields + * in a compressed format. + */ +class StringBuilderFieldUtils { +public: + struct CategoryFieldPair { + int32_t category; + int32_t field; + }; - int32_t trimBack(int32_t limit) const; + /** Compile-time function to construct a Field from a category and a field */ + template + static constexpr FormattedStringBuilder::Field compress() { + static_assert(category != 0, "cannot use Undefined category in FieldUtils"); + static_assert(category <= 0xf, "only 4 bits for category"); + static_assert(field <= 0xf, "only 4 bits for field"); + return static_cast((category << 4) | field); + } - int32_t trimFront(int32_t start) const; + /** Runtime inline function to unpack the category and field from the Field */ + static inline CategoryFieldPair expand(FormattedStringBuilder::Field field) { + if (field == UNUM_FIELD_COUNT) { + return {UFIELD_CATEGORY_UNDEFINED, 0}; + } + CategoryFieldPair ret = { + (field >> 4), + (field & 0xf) + }; + if (ret.category == 0) { + ret.category = UFIELD_CATEGORY_NUMBER; + } + return ret; + } + + static inline bool isNumericField(FormattedStringBuilder::Field field) { + int8_t category = field >> 4; + return category == 0 || category == UFIELD_CATEGORY_NUMBER; + } }; -} // namespace impl -} // namespace number U_NAMESPACE_END diff --git a/icu4c/source/i18n/formattedval_impl.h b/icu4c/source/i18n/formattedval_impl.h index ee15ed9dfd0..9aab36a52fe 100644 --- a/icu4c/source/i18n/formattedval_impl.h +++ b/icu4c/source/i18n/formattedval_impl.h @@ -18,7 +18,7 @@ #include "fphdlimp.h" #include "util.h" #include "uvectr32.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" /** @@ -67,7 +67,9 @@ typedef enum UCFPosConstraintType { U_NAMESPACE_BEGIN -/** Implementation using FieldPositionHandler to accept fields. */ +/** + * Implementation of FormattedValue using FieldPositionHandler to accept fields. + */ class FormattedValueFieldPositionIteratorImpl : public UMemory, public FormattedValue { public: @@ -112,12 +114,21 @@ private: }; -class FormattedValueNumberStringBuilderImpl : public UMemory, public FormattedValue { +/** + * Implementation of FormattedValue based on FormattedStringBuilder. + * + * The implementation currently revolves around numbers and number fields. + * However, it can be generalized in the future when there is a need. + * + * @author sffc (Shane Carr) + */ +// Exported as U_I18N_API for tests +class U_I18N_API FormattedValueStringBuilderImpl : public UMemory, public FormattedValue { public: - FormattedValueNumberStringBuilderImpl(number::impl::Field numericField); + FormattedValueStringBuilderImpl(FormattedStringBuilder::Field numericField); - virtual ~FormattedValueNumberStringBuilderImpl(); + virtual ~FormattedValueStringBuilderImpl(); // Implementation of FormattedValue (const): @@ -126,17 +137,25 @@ public: Appendable& appendTo(Appendable& appendable, UErrorCode& status) const U_OVERRIDE; UBool nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const U_OVERRIDE; - inline number::impl::NumberStringBuilder& getStringRef() { + // Additional helper functions: + UBool nextFieldPosition(FieldPosition& fp, UErrorCode& status) const; + void getAllFieldPositions(FieldPositionIteratorHandler& fpih, UErrorCode& status) const; + inline FormattedStringBuilder& getStringRef() { return fString; } - - inline const number::impl::NumberStringBuilder& getStringRef() const { + inline const FormattedStringBuilder& getStringRef() const { return fString; } private: - number::impl::NumberStringBuilder fString; - number::impl::Field fNumericField; + FormattedStringBuilder fString; + FormattedStringBuilder::Field fNumericField; + + bool nextPositionImpl(ConstrainedFieldPosition& cfpos, FormattedStringBuilder::Field numericField, UErrorCode& status) const; + static bool isIntOrGroup(FormattedStringBuilder::Field field); + static bool isNumericField(FormattedStringBuilder::Field field); + int32_t trimBack(int32_t limit) const; + int32_t trimFront(int32_t start) const; }; diff --git a/icu4c/source/i18n/formattedval_sbimpl.cpp b/icu4c/source/i18n/formattedval_sbimpl.cpp index 1fbecf25ac6..ca28f222813 100644 --- a/icu4c/source/i18n/formattedval_sbimpl.cpp +++ b/icu4c/source/i18n/formattedval_sbimpl.cpp @@ -9,35 +9,203 @@ // Other independent implementations should go into their own cpp file for // better dependency modularization. +#include "unicode/ustring.h" #include "formattedval_impl.h" +#include "number_types.h" +#include "formatted_string_builder.h" +#include "number_utils.h" +#include "static_unicode_sets.h" U_NAMESPACE_BEGIN -FormattedValueNumberStringBuilderImpl::FormattedValueNumberStringBuilderImpl(number::impl::Field numericField) +typedef FormattedStringBuilder::Field Field; + + +FormattedValueStringBuilderImpl::FormattedValueStringBuilderImpl(Field numericField) : fNumericField(numericField) { } -FormattedValueNumberStringBuilderImpl::~FormattedValueNumberStringBuilderImpl() { +FormattedValueStringBuilderImpl::~FormattedValueStringBuilderImpl() { } -UnicodeString FormattedValueNumberStringBuilderImpl::toString(UErrorCode&) const { +UnicodeString FormattedValueStringBuilderImpl::toString(UErrorCode&) const { return fString.toUnicodeString(); } -UnicodeString FormattedValueNumberStringBuilderImpl::toTempString(UErrorCode&) const { +UnicodeString FormattedValueStringBuilderImpl::toTempString(UErrorCode&) const { return fString.toTempUnicodeString(); } -Appendable& FormattedValueNumberStringBuilderImpl::appendTo(Appendable& appendable, UErrorCode&) const { +Appendable& FormattedValueStringBuilderImpl::appendTo(Appendable& appendable, UErrorCode&) const { appendable.appendString(fString.chars(), fString.length()); return appendable; } -UBool FormattedValueNumberStringBuilderImpl::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { +UBool FormattedValueStringBuilderImpl::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const { // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool - return fString.nextPosition(cfpos, fNumericField, status) ? TRUE : FALSE; + return nextPositionImpl(cfpos, fNumericField, status) ? TRUE : FALSE; +} + +UBool FormattedValueStringBuilderImpl::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const { + int32_t rawField = fp.getField(); + + if (rawField == FieldPosition::DONT_CARE) { + return FALSE; + } + + if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return FALSE; + } + + ConstrainedFieldPosition cfpos; + cfpos.constrainField(UFIELD_CATEGORY_NUMBER, rawField); + cfpos.setState(UFIELD_CATEGORY_NUMBER, rawField, fp.getBeginIndex(), fp.getEndIndex()); + if (nextPositionImpl(cfpos, 0, status)) { + fp.setBeginIndex(cfpos.getStart()); + fp.setEndIndex(cfpos.getLimit()); + return TRUE; + } + + // Special case: fraction should start after integer if fraction is not present + if (rawField == UNUM_FRACTION_FIELD && fp.getEndIndex() == 0) { + bool inside = false; + int32_t i = fString.fZero; + for (; i < fString.fZero + fString.fLength; i++) { + if (isIntOrGroup(fString.getFieldPtr()[i]) || fString.getFieldPtr()[i] == UNUM_DECIMAL_SEPARATOR_FIELD) { + inside = true; + } else if (inside) { + break; + } + } + fp.setBeginIndex(i - fString.fZero); + fp.setEndIndex(i - fString.fZero); + } + + return FALSE; +} + +void FormattedValueStringBuilderImpl::getAllFieldPositions(FieldPositionIteratorHandler& fpih, + UErrorCode& status) const { + ConstrainedFieldPosition cfpos; + while (nextPositionImpl(cfpos, 0, status)) { + fpih.addAttribute(cfpos.getField(), cfpos.getStart(), cfpos.getLimit()); + } +} + +// Signal the end of the string using a field that doesn't exist and that is +// different from UNUM_FIELD_COUNT, which is used for "null number field". +static constexpr Field kEndField = 0xff; + +bool FormattedValueStringBuilderImpl::nextPositionImpl(ConstrainedFieldPosition& cfpos, Field numericField, UErrorCode& /*status*/) const { + auto numericCAF = StringBuilderFieldUtils::expand(numericField); + int32_t fieldStart = -1; + Field currField = UNUM_FIELD_COUNT; + for (int32_t i = fString.fZero + cfpos.getLimit(); i <= fString.fZero + fString.fLength; i++) { + Field _field = (i < fString.fZero + fString.fLength) ? fString.getFieldPtr()[i] : kEndField; + // Case 1: currently scanning a field. + if (currField != UNUM_FIELD_COUNT) { + if (currField != _field) { + int32_t end = i - fString.fZero; + // Grouping separators can be whitespace; don't throw them out! + if (currField != UNUM_GROUPING_SEPARATOR_FIELD) { + end = trimBack(i - fString.fZero); + } + if (end <= fieldStart) { + // Entire field position is ignorable; skip. + fieldStart = -1; + currField = UNUM_FIELD_COUNT; + i--; // look at this index again + continue; + } + int32_t start = fieldStart; + if (currField != UNUM_GROUPING_SEPARATOR_FIELD) { + start = trimFront(start); + } + auto caf = StringBuilderFieldUtils::expand(currField); + cfpos.setState(caf.category, caf.field, start, end); + return true; + } + continue; + } + // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER. + if (cfpos.matchesField(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD) + && i > fString.fZero + // don't return the same field twice in a row: + && i - fString.fZero > cfpos.getLimit() + && isIntOrGroup(fString.getFieldPtr()[i - 1]) + && !isIntOrGroup(_field)) { + int j = i - 1; + for (; j >= fString.fZero && isIntOrGroup(fString.getFieldPtr()[j]); j--) {} + cfpos.setState( + UFIELD_CATEGORY_NUMBER, + UNUM_INTEGER_FIELD, + j - fString.fZero + 1, + i - fString.fZero); + return true; + } + // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC. + if (numericField != 0 + && cfpos.matchesField(numericCAF.category, numericCAF.field) + && i > fString.fZero + // don't return the same field twice in a row: + && (i - fString.fZero > cfpos.getLimit() + || cfpos.getCategory() != numericCAF.category + || cfpos.getField() != numericCAF.field) + && isNumericField(fString.getFieldPtr()[i - 1]) + && !isNumericField(_field)) { + int j = i - 1; + for (; j >= fString.fZero && isNumericField(fString.getFieldPtr()[j]); j--) {} + cfpos.setState( + numericCAF.category, + numericCAF.field, + j - fString.fZero + 1, + i - fString.fZero); + return true; + } + // Special case: skip over INTEGER; will be coalesced later. + if (_field == UNUM_INTEGER_FIELD) { + _field = UNUM_FIELD_COUNT; + } + // Case 2: no field starting at this position. + if (_field == UNUM_FIELD_COUNT || _field == kEndField) { + continue; + } + // Case 3: check for field starting at this position + auto caf = StringBuilderFieldUtils::expand(_field); + if (cfpos.matchesField(caf.category, caf.field)) { + fieldStart = i - fString.fZero; + currField = _field; + } + } + + U_ASSERT(currField == UNUM_FIELD_COUNT); + return false; +} + +bool FormattedValueStringBuilderImpl::isIntOrGroup(Field field) { + return field == UNUM_INTEGER_FIELD + || field == UNUM_GROUPING_SEPARATOR_FIELD; +} + +bool FormattedValueStringBuilderImpl::isNumericField(Field field) { + return StringBuilderFieldUtils::isNumericField(field); +} + +int32_t FormattedValueStringBuilderImpl::trimBack(int32_t limit) const { + return unisets::get(unisets::DEFAULT_IGNORABLES)->spanBack( + fString.getCharPtr() + fString.fZero, + limit, + USET_SPAN_CONTAINED); +} + +int32_t FormattedValueStringBuilderImpl::trimFront(int32_t start) const { + return start + unisets::get(unisets::DEFAULT_IGNORABLES)->span( + fString.getCharPtr() + fString.fZero + start, + fString.fLength - start, + USET_SPAN_CONTAINED); } diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj index d2264c1afa6..49ddc792ad8 100644 --- a/icu4c/source/i18n/i18n.vcxproj +++ b/icu4c/source/i18n/i18n.vcxproj @@ -278,14 +278,14 @@ - + - + @@ -541,7 +541,7 @@ - + @@ -549,7 +549,7 @@ - + diff --git a/icu4c/source/i18n/i18n.vcxproj.filters b/icu4c/source/i18n/i18n.vcxproj.filters index 5cf379952d8..cb393c695b4 100644 --- a/icu4c/source/i18n/i18n.vcxproj.filters +++ b/icu4c/source/i18n/i18n.vcxproj.filters @@ -585,7 +585,7 @@ formatting - + formatting @@ -606,7 +606,7 @@ formatting - + formatting @@ -878,7 +878,7 @@ formatting - + formatting @@ -902,7 +902,7 @@ formatting - + formatting diff --git a/icu4c/source/i18n/i18n_uwp.vcxproj b/icu4c/source/i18n/i18n_uwp.vcxproj index 4a62c194805..7580e2a4cb6 100644 --- a/icu4c/source/i18n/i18n_uwp.vcxproj +++ b/icu4c/source/i18n/i18n_uwp.vcxproj @@ -385,14 +385,14 @@ - + - + @@ -646,7 +646,7 @@ - + @@ -654,7 +654,7 @@ - + diff --git a/icu4c/source/i18n/number_affixutils.cpp b/icu4c/source/i18n/number_affixutils.cpp index 3eb9c59bf49..3b1b42fe353 100644 --- a/icu4c/source/i18n/number_affixutils.cpp +++ b/icu4c/source/i18n/number_affixutils.cpp @@ -156,7 +156,7 @@ Field AffixUtils::getFieldForType(AffixPatternType type) { } int32_t -AffixUtils::unescape(const UnicodeString &affixPattern, NumberStringBuilder &output, int32_t position, +AffixUtils::unescape(const UnicodeString &affixPattern, FormattedStringBuilder &output, int32_t position, const SymbolProvider &provider, Field field, UErrorCode &status) { int32_t length = 0; AffixTag tag; diff --git a/icu4c/source/i18n/number_affixutils.h b/icu4c/source/i18n/number_affixutils.h index f011a54b316..5cfde61ffd0 100644 --- a/icu4c/source/i18n/number_affixutils.h +++ b/icu4c/source/i18n/number_affixutils.h @@ -11,7 +11,7 @@ #include "number_types.h" #include "unicode/stringpiece.h" #include "unicode/unistr.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" #include "unicode/uniset.h" U_NAMESPACE_BEGIN namespace number { @@ -134,16 +134,16 @@ class U_I18N_API AffixUtils { /** * Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", "‰", and * "¤" with the corresponding symbols provided by the {@link SymbolProvider}, and inserts the - * result into the NumberStringBuilder at the requested location. + * result into the FormattedStringBuilder at the requested location. * *

Example input: "'-'¤x"; example output: "-$x" * * @param affixPattern The original string to be unescaped. - * @param output The NumberStringBuilder to mutate with the result. - * @param position The index into the NumberStringBuilder to insert the string. + * @param output The FormattedStringBuilder to mutate with the result. + * @param position The index into the FormattedStringBuilder to insert the string. * @param provider An object to generate locale symbols. */ - static int32_t unescape(const UnicodeString& affixPattern, NumberStringBuilder& output, + static int32_t unescape(const UnicodeString& affixPattern, FormattedStringBuilder& output, int32_t position, const SymbolProvider& provider, Field field, UErrorCode& status); diff --git a/icu4c/source/i18n/number_asformat.cpp b/icu4c/source/i18n/number_asformat.cpp index 9d3ea69f578..974dfba3c19 100644 --- a/icu4c/source/i18n/number_asformat.cpp +++ b/icu4c/source/i18n/number_asformat.cpp @@ -62,12 +62,12 @@ UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj, // always return first occurrence: pos.setBeginIndex(0); pos.setEndIndex(0); - bool found = data.getStringRef().nextFieldPosition(pos, status); + bool found = data.nextFieldPosition(pos, status); if (found && appendTo.length() != 0) { pos.setBeginIndex(pos.getBeginIndex() + appendTo.length()); pos.setEndIndex(pos.getEndIndex() + appendTo.length()); } - appendTo.append(data.getStringRef().toTempUnicodeString()); + appendTo.append(data.toTempString(status)); return appendTo; } @@ -84,10 +84,10 @@ UnicodeString& LocalizedNumberFormatterAsFormat::format(const Formattable& obj, if (U_FAILURE(status)) { return appendTo; } - appendTo.append(data.getStringRef().toTempUnicodeString()); + appendTo.append(data.toTempString(status)); if (posIter != nullptr) { FieldPositionIteratorHandler fpih(posIter, status); - data.getStringRef().getAllFieldPositions(fpih, status); + data.getAllFieldPositions(fpih, status); } return appendTo; } diff --git a/icu4c/source/i18n/number_fluent.cpp b/icu4c/source/i18n/number_fluent.cpp index 09e0905609e..bd49a121013 100644 --- a/icu4c/source/i18n/number_fluent.cpp +++ b/icu4c/source/i18n/number_fluent.cpp @@ -696,7 +696,7 @@ void LocalizedNumberFormatter::formatImpl(impl::UFormattedNumberData* results, U void LocalizedNumberFormatter::getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result, UErrorCode& status) const { - NumberStringBuilder string; + FormattedStringBuilder string; auto signum = static_cast(isNegative ? -1 : 1); // Always return affixes for plural form OTHER. static const StandardPlural::Form plural = StandardPlural::OTHER; diff --git a/icu4c/source/i18n/number_formatimpl.cpp b/icu4c/source/i18n/number_formatimpl.cpp index 08b833beb7a..8741af39df1 100644 --- a/icu4c/source/i18n/number_formatimpl.cpp +++ b/icu4c/source/i18n/number_formatimpl.cpp @@ -72,7 +72,7 @@ NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& s } int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuantity& inValue, - NumberStringBuilder& outString, UErrorCode& status) { + FormattedStringBuilder& outString, UErrorCode& status) { NumberFormatterImpl impl(macros, false, status); MicroProps& micros = impl.preProcessUnsafe(inValue, status); if (U_FAILURE(status)) { return 0; } @@ -83,7 +83,7 @@ int32_t NumberFormatterImpl::formatStatic(const MacroProps& macros, DecimalQuant int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int8_t signum, StandardPlural::Form plural, - NumberStringBuilder& outString, UErrorCode& status) { + FormattedStringBuilder& outString, UErrorCode& status) { NumberFormatterImpl impl(macros, false, status); return impl.getPrefixSuffixUnsafe(signum, plural, outString, status); } @@ -93,7 +93,7 @@ int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, int // The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation. // See MicroProps::processQuantity() for details. -int32_t NumberFormatterImpl::format(DecimalQuantity& inValue, NumberStringBuilder& outString, +int32_t NumberFormatterImpl::format(DecimalQuantity& inValue, FormattedStringBuilder& outString, UErrorCode& status) const { MicroProps micros; preProcess(inValue, micros, status); @@ -130,7 +130,7 @@ MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErr } int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form plural, - NumberStringBuilder& outString, UErrorCode& status) const { + FormattedStringBuilder& outString, UErrorCode& status) const { if (U_FAILURE(status)) { return 0; } // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier). // Safe path: use fImmutablePatternModifier. @@ -141,7 +141,7 @@ int32_t NumberFormatterImpl::getPrefixSuffix(int8_t signum, StandardPlural::Form } int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural, - NumberStringBuilder& outString, UErrorCode& status) { + FormattedStringBuilder& outString, UErrorCode& status) { if (U_FAILURE(status)) { return 0; } // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier). // Unsafe path: use fPatternModifier. @@ -430,7 +430,7 @@ NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Local return fRules.getAlias(); } -int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, NumberStringBuilder& string, +int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string, int32_t start, int32_t end, UErrorCode& status) { // Always apply the inner modifier (which is "strong"). int32_t length = micros.modInner->apply(string, start, end, status); @@ -445,7 +445,7 @@ int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, NumberString } int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity, - NumberStringBuilder& string, int32_t index, + FormattedStringBuilder& string, int32_t index, UErrorCode& status) { int32_t length = 0; if (quantity.isInfinite()) { @@ -487,7 +487,7 @@ int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuanti } int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity, - NumberStringBuilder& string, int32_t index, + FormattedStringBuilder& string, int32_t index, UErrorCode& status) { int length = 0; int integerCount = quantity.getUpperDisplayMagnitude() + 1; @@ -513,7 +513,7 @@ int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, Decima } int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity, - NumberStringBuilder& string, int32_t index, + FormattedStringBuilder& string, int32_t index, UErrorCode& status) { int length = 0; int fractionCount = -quantity.getLowerDisplayMagnitude(); diff --git a/icu4c/source/i18n/number_formatimpl.h b/icu4c/source/i18n/number_formatimpl.h index fd8708c532e..44753c0392f 100644 --- a/icu4c/source/i18n/number_formatimpl.h +++ b/icu4c/source/i18n/number_formatimpl.h @@ -8,7 +8,7 @@ #define __NUMBER_FORMATIMPL_H__ #include "number_types.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" #include "number_patternstring.h" #include "number_utils.h" #include "number_patternmodifier.h" @@ -35,7 +35,7 @@ class NumberFormatterImpl : public UMemory { * Builds and evaluates an "unsafe" MicroPropsGenerator, which is cheaper but can be used only once. */ static int32_t - formatStatic(const MacroProps ¯os, DecimalQuantity &inValue, NumberStringBuilder &outString, + formatStatic(const MacroProps ¯os, DecimalQuantity &inValue, FormattedStringBuilder &outString, UErrorCode &status); /** @@ -45,13 +45,13 @@ class NumberFormatterImpl : public UMemory { * the prefix length. */ static int32_t getPrefixSuffixStatic(const MacroProps& macros, int8_t signum, - StandardPlural::Form plural, NumberStringBuilder& outString, + StandardPlural::Form plural, FormattedStringBuilder& outString, UErrorCode& status); /** * Evaluates the "safe" MicroPropsGenerator created by "fromMacros". */ - int32_t format(DecimalQuantity& inValue, NumberStringBuilder& outString, UErrorCode& status) const; + int32_t format(DecimalQuantity& inValue, FormattedStringBuilder& outString, UErrorCode& status) const; /** * Like format(), but saves the result into an output MicroProps without additional processing. @@ -61,7 +61,7 @@ class NumberFormatterImpl : public UMemory { /** * Like getPrefixSuffixStatic() but uses the safe compiled object. */ - int32_t getPrefixSuffix(int8_t signum, StandardPlural::Form plural, NumberStringBuilder& outString, + int32_t getPrefixSuffix(int8_t signum, StandardPlural::Form plural, FormattedStringBuilder& outString, UErrorCode& status) const; const MicroProps& getRawMicroProps() const { @@ -73,12 +73,12 @@ class NumberFormatterImpl : public UMemory { * This method formats only the main number, not affixes. */ static int32_t writeNumber(const MicroProps& micros, DecimalQuantity& quantity, - NumberStringBuilder& string, int32_t index, UErrorCode& status); + FormattedStringBuilder& string, int32_t index, UErrorCode& status); /** * Adds the affixes. Intended to be called immediately after formatNumber. */ - static int32_t writeAffixes(const MicroProps& micros, NumberStringBuilder& string, int32_t start, + static int32_t writeAffixes(const MicroProps& micros, FormattedStringBuilder& string, int32_t start, int32_t end, UErrorCode& status); private: @@ -110,7 +110,7 @@ class NumberFormatterImpl : public UMemory { MicroProps& preProcessUnsafe(DecimalQuantity &inValue, UErrorCode &status); int32_t getPrefixSuffixUnsafe(int8_t signum, StandardPlural::Form plural, - NumberStringBuilder& outString, UErrorCode& status); + FormattedStringBuilder& outString, UErrorCode& status); /** * If rulesPtr is non-null, return it. Otherwise, return a PluralRules owned by this object for the @@ -136,11 +136,11 @@ class NumberFormatterImpl : public UMemory { macrosToMicroGenerator(const MacroProps ¯os, bool safe, UErrorCode &status); static int32_t - writeIntegerDigits(const MicroProps µs, DecimalQuantity &quantity, NumberStringBuilder &string, + writeIntegerDigits(const MicroProps µs, DecimalQuantity &quantity, FormattedStringBuilder &string, int32_t index, UErrorCode &status); static int32_t - writeFractionDigits(const MicroProps µs, DecimalQuantity &quantity, NumberStringBuilder &string, + writeFractionDigits(const MicroProps µs, DecimalQuantity &quantity, FormattedStringBuilder &string, int32_t index, UErrorCode &status); }; diff --git a/icu4c/source/i18n/number_modifiers.cpp b/icu4c/source/i18n/number_modifiers.cpp index 1fcbe7b9b79..aa6cf57d235 100644 --- a/icu4c/source/i18n/number_modifiers.cpp +++ b/icu4c/source/i18n/number_modifiers.cpp @@ -69,7 +69,7 @@ AdoptingModifierStore::~AdoptingModifierStore() { } -int32_t ConstantAffixModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex, +int32_t ConstantAffixModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, UErrorCode &status) const { // Insert the suffix first since inserting the prefix will change the rightIndex int length = output.insert(rightIndex, fSuffix, fField, status); @@ -154,7 +154,7 @@ SimpleModifier::SimpleModifier() : fField(UNUM_FIELD_COUNT), fStrong(false), fPrefixLength(0), fSuffixLength(0) { } -int32_t SimpleModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex, +int32_t SimpleModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, UErrorCode &status) const { return formatAsPrefixSuffix(output, leftIndex, rightIndex, status); } @@ -203,7 +203,7 @@ bool SimpleModifier::semanticallyEquivalent(const Modifier& other) const { int32_t -SimpleModifier::formatAsPrefixSuffix(NumberStringBuilder &result, int32_t startIndex, int32_t endIndex, +SimpleModifier::formatAsPrefixSuffix(FormattedStringBuilder &result, int32_t startIndex, int32_t endIndex, UErrorCode &status) const { if (fSuffixOffset == -1 && fPrefixLength + fSuffixLength > 0) { // There is no argument for the inner number; overwrite the entire segment with our string. @@ -227,7 +227,7 @@ SimpleModifier::formatAsPrefixSuffix(NumberStringBuilder &result, int32_t startI int32_t -SimpleModifier::formatTwoArgPattern(const SimpleFormatter& compiled, NumberStringBuilder& result, +SimpleModifier::formatTwoArgPattern(const SimpleFormatter& compiled, FormattedStringBuilder& result, int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength, Field field, UErrorCode& status) { const UnicodeString& compiledPattern = compiled.compiledPattern; @@ -284,7 +284,7 @@ SimpleModifier::formatTwoArgPattern(const SimpleFormatter& compiled, NumberStrin } -int32_t ConstantMultiFieldModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex, +int32_t ConstantMultiFieldModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, UErrorCode &status) const { int32_t length = output.insert(leftIndex, fPrefix, status); if (fOverwrite) { @@ -333,8 +333,8 @@ bool ConstantMultiFieldModifier::semanticallyEquivalent(const Modifier& other) c } -CurrencySpacingEnabledModifier::CurrencySpacingEnabledModifier(const NumberStringBuilder &prefix, - const NumberStringBuilder &suffix, +CurrencySpacingEnabledModifier::CurrencySpacingEnabledModifier(const FormattedStringBuilder &prefix, + const FormattedStringBuilder &suffix, bool overwrite, bool strong, const DecimalFormatSymbols &symbols, @@ -374,7 +374,7 @@ CurrencySpacingEnabledModifier::CurrencySpacingEnabledModifier(const NumberStrin } } -int32_t CurrencySpacingEnabledModifier::apply(NumberStringBuilder &output, int leftIndex, int rightIndex, +int32_t CurrencySpacingEnabledModifier::apply(FormattedStringBuilder &output, int leftIndex, int rightIndex, UErrorCode &status) const { // Currency spacing logic int length = 0; @@ -395,7 +395,7 @@ int32_t CurrencySpacingEnabledModifier::apply(NumberStringBuilder &output, int l } int32_t -CurrencySpacingEnabledModifier::applyCurrencySpacing(NumberStringBuilder &output, int32_t prefixStart, +CurrencySpacingEnabledModifier::applyCurrencySpacing(FormattedStringBuilder &output, int32_t prefixStart, int32_t prefixLen, int32_t suffixStart, int32_t suffixLen, const DecimalFormatSymbols &symbols, @@ -414,7 +414,7 @@ CurrencySpacingEnabledModifier::applyCurrencySpacing(NumberStringBuilder &output } int32_t -CurrencySpacingEnabledModifier::applyCurrencySpacingAffix(NumberStringBuilder &output, int32_t index, +CurrencySpacingEnabledModifier::applyCurrencySpacingAffix(FormattedStringBuilder &output, int32_t index, EAffix affix, const DecimalFormatSymbols &symbols, UErrorCode &status) { diff --git a/icu4c/source/i18n/number_modifiers.h b/icu4c/source/i18n/number_modifiers.h index 495128bb149..5c13c873463 100644 --- a/icu4c/source/i18n/number_modifiers.h +++ b/icu4c/source/i18n/number_modifiers.h @@ -12,7 +12,7 @@ #include "unicode/uniset.h" #include "unicode/simpleformatter.h" #include "standardplural.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" #include "number_types.h" U_NAMESPACE_BEGIN namespace number { @@ -28,7 +28,7 @@ class U_I18N_API ConstantAffixModifier : public Modifier, public UObject { bool strong) : fPrefix(prefix), fSuffix(suffix), fField(field), fStrong(strong) {} - int32_t apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, UErrorCode &status) const U_OVERRIDE; int32_t getPrefixLength() const U_OVERRIDE; @@ -64,7 +64,7 @@ class U_I18N_API SimpleModifier : public Modifier, public UMemory { // Default constructor for LongNameHandler.h SimpleModifier(); - int32_t apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, UErrorCode &status) const U_OVERRIDE; int32_t getPrefixLength() const U_OVERRIDE; @@ -81,7 +81,7 @@ class U_I18N_API SimpleModifier : public Modifier, public UMemory { /** * TODO: This belongs in SimpleFormatterImpl. The only reason I haven't moved it there yet is because - * NumberStringBuilder is an internal class and SimpleFormatterImpl feels like it should not depend on it. + * FormattedStringBuilder is an internal class and SimpleFormatterImpl feels like it should not depend on it. * *

* Formats a value that is already stored inside the StringBuilder result between the indices @@ -100,22 +100,22 @@ class U_I18N_API SimpleModifier : public Modifier, public UMemory { * @return The number of characters (UTF-16 code points) that were added to the StringBuilder. */ int32_t - formatAsPrefixSuffix(NumberStringBuilder& result, int32_t startIndex, int32_t endIndex, + formatAsPrefixSuffix(FormattedStringBuilder& result, int32_t startIndex, int32_t endIndex, UErrorCode& status) const; /** * TODO: Like above, this belongs with the rest of the SimpleFormatterImpl code. - * I put it here so that the SimpleFormatter uses in NumberStringBuilder are near each other. + * I put it here so that the SimpleFormatter uses in FormattedStringBuilder are near each other. * *

- * Applies the compiled two-argument pattern to the NumberStringBuilder. + * Applies the compiled two-argument pattern to the FormattedStringBuilder. * *

* This method is optimized for the case where the prefix and suffix are often empty, such as * in the range pattern like "{0}-{1}". */ static int32_t - formatTwoArgPattern(const SimpleFormatter& compiled, NumberStringBuilder& result, + formatTwoArgPattern(const SimpleFormatter& compiled, FormattedStringBuilder& result, int32_t index, int32_t* outPrefixLength, int32_t* outSuffixLength, Field field, UErrorCode& status); @@ -131,13 +131,13 @@ class U_I18N_API SimpleModifier : public Modifier, public UMemory { /** * An implementation of {@link Modifier} that allows for multiple types of fields in the same modifier. Constructed - * based on the contents of two {@link NumberStringBuilder} instances (one for the prefix, one for the suffix). + * based on the contents of two {@link FormattedStringBuilder} instances (one for the prefix, one for the suffix). */ class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory { public: ConstantMultiFieldModifier( - const NumberStringBuilder &prefix, - const NumberStringBuilder &suffix, + const FormattedStringBuilder &prefix, + const FormattedStringBuilder &suffix, bool overwrite, bool strong, const Modifier::Parameters parameters) @@ -148,8 +148,8 @@ class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory { fParameters(parameters) {} ConstantMultiFieldModifier( - const NumberStringBuilder &prefix, - const NumberStringBuilder &suffix, + const FormattedStringBuilder &prefix, + const FormattedStringBuilder &suffix, bool overwrite, bool strong) : fPrefix(prefix), @@ -157,7 +157,7 @@ class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory { fOverwrite(overwrite), fStrong(strong) {} - int32_t apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, UErrorCode &status) const U_OVERRIDE; int32_t getPrefixLength() const U_OVERRIDE; @@ -173,10 +173,10 @@ class U_I18N_API ConstantMultiFieldModifier : public Modifier, public UMemory { bool semanticallyEquivalent(const Modifier& other) const U_OVERRIDE; protected: - // NOTE: In Java, these are stored as array pointers. In C++, the NumberStringBuilder is stored by + // NOTE: In Java, these are stored as array pointers. In C++, the FormattedStringBuilder is stored by // value and is treated internally as immutable. - NumberStringBuilder fPrefix; - NumberStringBuilder fSuffix; + FormattedStringBuilder fPrefix; + FormattedStringBuilder fSuffix; bool fOverwrite; bool fStrong; Modifier::Parameters fParameters; @@ -187,19 +187,19 @@ class U_I18N_API CurrencySpacingEnabledModifier : public ConstantMultiFieldModif public: /** Safe code path */ CurrencySpacingEnabledModifier( - const NumberStringBuilder &prefix, - const NumberStringBuilder &suffix, + const FormattedStringBuilder &prefix, + const FormattedStringBuilder &suffix, bool overwrite, bool strong, const DecimalFormatSymbols &symbols, UErrorCode &status); - int32_t apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, UErrorCode &status) const U_OVERRIDE; /** Unsafe code path */ static int32_t - applyCurrencySpacing(NumberStringBuilder &output, int32_t prefixStart, int32_t prefixLen, + applyCurrencySpacing(FormattedStringBuilder &output, int32_t prefixStart, int32_t prefixLen, int32_t suffixStart, int32_t suffixLen, const DecimalFormatSymbols &symbols, UErrorCode &status); @@ -218,7 +218,7 @@ class U_I18N_API CurrencySpacingEnabledModifier : public ConstantMultiFieldModif }; /** Unsafe code path */ - static int32_t applyCurrencySpacingAffix(NumberStringBuilder &output, int32_t index, EAffix affix, + static int32_t applyCurrencySpacingAffix(FormattedStringBuilder &output, int32_t index, EAffix affix, const DecimalFormatSymbols &symbols, UErrorCode &status); static UnicodeSet @@ -234,7 +234,7 @@ class U_I18N_API EmptyModifier : public Modifier, public UMemory { public: explicit EmptyModifier(bool isStrong) : fStrong(isStrong) {} - int32_t apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, UErrorCode &status) const U_OVERRIDE { (void)output; (void)leftIndex; diff --git a/icu4c/source/i18n/number_output.cpp b/icu4c/source/i18n/number_output.cpp index 1e86f2f9833..888a9f25463 100644 --- a/icu4c/source/i18n/number_output.cpp +++ b/icu4c/source/i18n/number_output.cpp @@ -19,8 +19,7 @@ UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumber) UBool FormattedNumber::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const { UPRV_FORMATTED_VALUE_METHOD_GUARD(FALSE) - // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool - return fData->getStringRef().nextFieldPosition(fieldPosition, status) ? TRUE : FALSE; + return fData->nextFieldPosition(fieldPosition, status); } void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const { @@ -31,7 +30,7 @@ void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErr void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih, UErrorCode& status) const { UPRV_FORMATTED_VALUE_METHOD_GUARD() - fData->getStringRef().getAllFieldPositions(fpih, status); + fData->getAllFieldPositions(fpih, status); } void FormattedNumber::getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const { diff --git a/icu4c/source/i18n/number_padding.cpp b/icu4c/source/i18n/number_padding.cpp index 31684d7208b..c68a9875b20 100644 --- a/icu4c/source/i18n/number_padding.cpp +++ b/icu4c/source/i18n/number_padding.cpp @@ -7,7 +7,7 @@ #include "unicode/numberformatter.h" #include "number_types.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" #include "number_decimfmtprops.h" using namespace icu; @@ -17,7 +17,7 @@ using namespace icu::number::impl; namespace { int32_t -addPaddingHelper(UChar32 paddingCp, int32_t requiredPadding, NumberStringBuilder &string, int32_t index, +addPaddingHelper(UChar32 paddingCp, int32_t requiredPadding, FormattedStringBuilder &string, int32_t index, UErrorCode &status) { for (int32_t i = 0; i < requiredPadding; i++) { // TODO: If appending to the end, this will cause actual insertion operations. Improve. @@ -60,7 +60,7 @@ Padder Padder::forProperties(const DecimalFormatProperties& properties) { } int32_t Padder::padAndApply(const Modifier &mod1, const Modifier &mod2, - NumberStringBuilder &string, int32_t leftIndex, int32_t rightIndex, + FormattedStringBuilder &string, int32_t leftIndex, int32_t rightIndex, UErrorCode &status) const { int32_t modLength = mod1.getCodePointCount() + mod2.getCodePointCount(); int32_t requiredPadding = fWidth - modLength - string.codePointCount(); diff --git a/icu4c/source/i18n/number_patternmodifier.cpp b/icu4c/source/i18n/number_patternmodifier.cpp index 75de439f3ed..8f3af2a5452 100644 --- a/icu4c/source/i18n/number_patternmodifier.cpp +++ b/icu4c/source/i18n/number_patternmodifier.cpp @@ -108,8 +108,8 @@ MutablePatternModifier::createImmutableAndChain(const MicroPropsGenerator* paren } ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) { - NumberStringBuilder a; - NumberStringBuilder b; + FormattedStringBuilder a; + FormattedStringBuilder b; insertPrefix(a, 0, status); insertSuffix(b, 0, status); if (fPatternInfo->hasCurrencySign()) { @@ -170,7 +170,7 @@ void MutablePatternModifier::processQuantity(DecimalQuantity& fq, MicroProps& mi micros.modMiddle = this; } -int32_t MutablePatternModifier::apply(NumberStringBuilder& output, int32_t leftIndex, int32_t rightIndex, +int32_t MutablePatternModifier::apply(FormattedStringBuilder& output, int32_t leftIndex, int32_t rightIndex, UErrorCode& status) const { // The unsafe code path performs self-mutation, so we need a const_cast. // This method needs to be const because it overrides a const method in the parent class. @@ -248,13 +248,13 @@ bool MutablePatternModifier::semanticallyEquivalent(const Modifier& other) const UPRV_UNREACHABLE; } -int32_t MutablePatternModifier::insertPrefix(NumberStringBuilder& sb, int position, UErrorCode& status) { +int32_t MutablePatternModifier::insertPrefix(FormattedStringBuilder& sb, int position, UErrorCode& status) { prepareAffix(true); int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status); return length; } -int32_t MutablePatternModifier::insertSuffix(NumberStringBuilder& sb, int position, UErrorCode& status) { +int32_t MutablePatternModifier::insertSuffix(FormattedStringBuilder& sb, int position, UErrorCode& status) { prepareAffix(false); int32_t length = AffixUtils::unescape(currentAffix, sb, position, *this, fField, status); return length; diff --git a/icu4c/source/i18n/number_patternmodifier.h b/icu4c/source/i18n/number_patternmodifier.h index 27e293b64ce..59ea02fe99c 100644 --- a/icu4c/source/i18n/number_patternmodifier.h +++ b/icu4c/source/i18n/number_patternmodifier.h @@ -184,7 +184,7 @@ class U_I18N_API MutablePatternModifier void processQuantity(DecimalQuantity &, MicroProps µs, UErrorCode &status) const U_OVERRIDE; - int32_t apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, UErrorCode &status) const U_OVERRIDE; int32_t getPrefixLength() const U_OVERRIDE; @@ -240,17 +240,17 @@ class U_I18N_API MutablePatternModifier * CREATES A NEW HEAP OBJECT; THE CALLER GETS OWNERSHIP. * * @param a - * A working NumberStringBuilder object; passed from the outside to prevent the need to create many new + * A working FormattedStringBuilder object; passed from the outside to prevent the need to create many new * instances if this method is called in a loop. * @param b - * Another working NumberStringBuilder object. + * Another working FormattedStringBuilder object. * @return The constant modifier object. */ ConstantMultiFieldModifier *createConstantModifier(UErrorCode &status); - int32_t insertPrefix(NumberStringBuilder &sb, int position, UErrorCode &status); + int32_t insertPrefix(FormattedStringBuilder &sb, int position, UErrorCode &status); - int32_t insertSuffix(NumberStringBuilder &sb, int position, UErrorCode &status); + int32_t insertSuffix(FormattedStringBuilder &sb, int position, UErrorCode &status); void prepareAffix(bool isPrefix); }; diff --git a/icu4c/source/i18n/number_scientific.cpp b/icu4c/source/i18n/number_scientific.cpp index 7fab29c36bd..6fb4c19b03d 100644 --- a/icu4c/source/i18n/number_scientific.cpp +++ b/icu4c/source/i18n/number_scientific.cpp @@ -8,7 +8,7 @@ #include #include "number_scientific.h" #include "number_utils.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" #include "unicode/unum.h" #include "number_microprops.h" @@ -36,7 +36,7 @@ void ScientificModifier::set(int32_t exponent, const ScientificHandler *handler) fHandler = handler; } -int32_t ScientificModifier::apply(NumberStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex, +int32_t ScientificModifier::apply(FormattedStringBuilder &output, int32_t /*leftIndex*/, int32_t rightIndex, UErrorCode &status) const { // FIXME: Localized exponent separator location. int i = rightIndex; diff --git a/icu4c/source/i18n/number_scientific.h b/icu4c/source/i18n/number_scientific.h index e377bd941ef..1c9ce1efa80 100644 --- a/icu4c/source/i18n/number_scientific.h +++ b/icu4c/source/i18n/number_scientific.h @@ -21,7 +21,7 @@ class U_I18N_API ScientificModifier : public UMemory, public Modifier { void set(int32_t exponent, const ScientificHandler *handler); - int32_t apply(NumberStringBuilder &output, int32_t leftIndex, int32_t rightIndex, + int32_t apply(FormattedStringBuilder &output, int32_t leftIndex, int32_t rightIndex, UErrorCode &status) const U_OVERRIDE; int32_t getPrefixLength() const U_OVERRIDE; diff --git a/icu4c/source/i18n/number_skeletons.cpp b/icu4c/source/i18n/number_skeletons.cpp index 7e06cab009b..c65a393215d 100644 --- a/icu4c/source/i18n/number_skeletons.cpp +++ b/icu4c/source/i18n/number_skeletons.cpp @@ -20,6 +20,7 @@ #include "unicode/numberformatter.h" #include "uinvchar.h" #include "charstr.h" +#include "string_segment.h" using namespace icu; using namespace icu::number; diff --git a/icu4c/source/i18n/number_skeletons.h b/icu4c/source/i18n/number_skeletons.h index bc228bd0d74..59af771928f 100644 --- a/icu4c/source/i18n/number_skeletons.h +++ b/icu4c/source/i18n/number_skeletons.h @@ -10,10 +10,10 @@ #include "number_types.h" #include "numparse_types.h" #include "unicode/ucharstrie.h" +#include "string_segment.h" -using icu::numparse::impl::StringSegment; - -U_NAMESPACE_BEGIN namespace number { +U_NAMESPACE_BEGIN +namespace number { namespace impl { // Forward-declaration diff --git a/icu4c/source/i18n/number_types.h b/icu4c/source/i18n/number_types.h index 225d1e57750..5aeead73e70 100644 --- a/icu4c/source/i18n/number_types.h +++ b/icu4c/source/i18n/number_types.h @@ -17,17 +17,16 @@ #include "unicode/platform.h" #include "unicode/uniset.h" #include "standardplural.h" +#include "formatted_string_builder.h" -U_NAMESPACE_BEGIN namespace number { +U_NAMESPACE_BEGIN +namespace number { namespace impl { -// Typedef several enums for brevity and for easier comparison to Java. +// For convenience and historical reasons, import the Field typedef to the namespace. +typedef FormattedStringBuilder::Field Field; -// Convention: bottom 4 bits for field, top 4 bits for field category. -// Field category 0 implies the number category so that the number field -// literals can be directly passed as a Field type. -// See the helper functions in "NumFieldUtils" in number_utils.h -typedef uint8_t Field; +// Typedef several enums for brevity and for easier comparison to Java. typedef UNumberFormatRoundingMode RoundingMode; @@ -49,7 +48,6 @@ static constexpr char16_t kFallbackPaddingString[] = u" "; class Modifier; class MutablePatternModifier; class DecimalQuantity; -class NumberStringBuilder; class ModifierStore; struct MicroProps; @@ -160,7 +158,7 @@ class U_I18N_API Modifier { * formatted. * @return The number of characters (UTF-16 code units) that were added to the string builder. */ - virtual int32_t apply(NumberStringBuilder& output, int leftIndex, int rightIndex, + virtual int32_t apply(FormattedStringBuilder& output, int leftIndex, int rightIndex, UErrorCode& status) const = 0; /** diff --git a/icu4c/source/i18n/number_utils.h b/icu4c/source/i18n/number_utils.h index 203dec6d83b..93195f080b2 100644 --- a/icu4c/source/i18n/number_utils.h +++ b/icu4c/source/i18n/number_utils.h @@ -17,6 +17,7 @@ #include "number_roundingutils.h" #include "decNumber.h" #include "charstr.h" +#include "formatted_string_builder.h" U_NAMESPACE_BEGIN @@ -32,52 +33,10 @@ enum CldrPatternStyle { CLDR_PATTERN_STYLE_COUNT, }; - -/** - * Helper functions for dealing with the Field typedef, which stores fields - * in a compressed format. - */ -class NumFieldUtils { -public: - struct CategoryFieldPair { - int32_t category; - int32_t field; - }; - - /** Compile-time function to construct a Field from a category and a field */ - template - static constexpr Field compress() { - static_assert(category != 0, "cannot use Undefined category in NumFieldUtils"); - static_assert(category <= 0xf, "only 4 bits for category"); - static_assert(field <= 0xf, "only 4 bits for field"); - return static_cast((category << 4) | field); - } - - /** Runtime inline function to unpack the category and field from the Field */ - static inline CategoryFieldPair expand(Field field) { - if (field == UNUM_FIELD_COUNT) { - return {UFIELD_CATEGORY_UNDEFINED, 0}; - } - CategoryFieldPair ret = { - (field >> 4), - (field & 0xf) - }; - if (ret.category == 0) { - ret.category = UFIELD_CATEGORY_NUMBER; - } - return ret; - } - - static inline bool isNumericField(Field field) { - int8_t category = field >> 4; - return category == 0 || category == UFIELD_CATEGORY_NUMBER; - } -}; - // Namespace for naked functions namespace utils { -inline int32_t insertDigitFromSymbols(NumberStringBuilder& output, int32_t index, int8_t digit, +inline int32_t insertDigitFromSymbols(FormattedStringBuilder& output, int32_t index, int8_t digit, const DecimalFormatSymbols& symbols, Field field, UErrorCode& status) { if (symbols.getCodePointZero() != -1) { diff --git a/icu4c/source/i18n/number_utypes.h b/icu4c/source/i18n/number_utypes.h index 88b493cbc25..6dbe5bee68f 100644 --- a/icu4c/source/i18n/number_utypes.h +++ b/icu4c/source/i18n/number_utypes.h @@ -10,7 +10,7 @@ #include "unicode/numberformatter.h" #include "number_types.h" #include "number_decimalquantity.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" #include "formattedval_impl.h" U_NAMESPACE_BEGIN namespace number { @@ -31,9 +31,9 @@ const DecimalQuantity* validateUFormattedNumberToDecimalQuantity( * The DecimalQuantity is not currently being used by FormattedNumber, but at some point it could be used * to add a toDecNumber() or similar method. */ -class UFormattedNumberData : public FormattedValueNumberStringBuilderImpl { +class UFormattedNumberData : public FormattedValueStringBuilderImpl { public: - UFormattedNumberData() : FormattedValueNumberStringBuilderImpl(0) {} + UFormattedNumberData() : FormattedValueStringBuilderImpl(0) {} virtual ~UFormattedNumberData(); DecimalQuantity quantity; diff --git a/icu4c/source/i18n/numparse_affixes.cpp b/icu4c/source/i18n/numparse_affixes.cpp index 6e00d9f6be1..6dc30d22414 100644 --- a/icu4c/source/i18n/numparse_affixes.cpp +++ b/icu4c/source/i18n/numparse_affixes.cpp @@ -13,6 +13,7 @@ #include "numparse_affixes.h" #include "numparse_utils.h" #include "number_utils.h" +#include "string_segment.h" using namespace icu; using namespace icu::numparse; diff --git a/icu4c/source/i18n/numparse_compositions.cpp b/icu4c/source/i18n/numparse_compositions.cpp index 19253da805f..2f7e1ab28d1 100644 --- a/icu4c/source/i18n/numparse_compositions.cpp +++ b/icu4c/source/i18n/numparse_compositions.cpp @@ -11,6 +11,7 @@ #include "numparse_types.h" #include "numparse_compositions.h" +#include "string_segment.h" #include "unicode/uniset.h" using namespace icu; diff --git a/icu4c/source/i18n/numparse_currency.cpp b/icu4c/source/i18n/numparse_currency.cpp index 598ace56533..6b53a73edf3 100644 --- a/icu4c/source/i18n/numparse_currency.cpp +++ b/icu4c/source/i18n/numparse_currency.cpp @@ -14,6 +14,7 @@ #include "ucurrimp.h" #include "unicode/errorcode.h" #include "numparse_utils.h" +#include "string_segment.h" using namespace icu; using namespace icu::numparse; diff --git a/icu4c/source/i18n/numparse_decimal.cpp b/icu4c/source/i18n/numparse_decimal.cpp index b120c5c6ad2..cf1e8156726 100644 --- a/icu4c/source/i18n/numparse_decimal.cpp +++ b/icu4c/source/i18n/numparse_decimal.cpp @@ -16,6 +16,7 @@ #include "unicode/uchar.h" #include "putilimp.h" #include "number_decimalquantity.h" +#include "string_segment.h" using namespace icu; using namespace icu::numparse; diff --git a/icu4c/source/i18n/numparse_impl.h b/icu4c/source/i18n/numparse_impl.h index 7d5f0b6f0bd..380d9aa4c64 100644 --- a/icu4c/source/i18n/numparse_impl.h +++ b/icu4c/source/i18n/numparse_impl.h @@ -18,6 +18,7 @@ #include "unicode/localpointer.h" #include "numparse_validators.h" #include "number_multiplier.h" +#include "string_segment.h" U_NAMESPACE_BEGIN diff --git a/icu4c/source/i18n/numparse_parsednumber.cpp b/icu4c/source/i18n/numparse_parsednumber.cpp index 3145f718dc2..6485d17fd03 100644 --- a/icu4c/source/i18n/numparse_parsednumber.cpp +++ b/icu4c/source/i18n/numparse_parsednumber.cpp @@ -11,6 +11,7 @@ #include "numparse_types.h" #include "number_decimalquantity.h" +#include "string_segment.h" #include "putilimp.h" #include diff --git a/icu4c/source/i18n/numparse_scientific.cpp b/icu4c/source/i18n/numparse_scientific.cpp index de389574408..b83b693f750 100644 --- a/icu4c/source/i18n/numparse_scientific.cpp +++ b/icu4c/source/i18n/numparse_scientific.cpp @@ -12,6 +12,7 @@ #include "numparse_types.h" #include "numparse_scientific.h" #include "static_unicode_sets.h" +#include "string_segment.h" using namespace icu; using namespace icu::numparse; diff --git a/icu4c/source/i18n/numparse_stringsegment.h b/icu4c/source/i18n/numparse_stringsegment.h deleted file mode 100644 index 7a84444d414..00000000000 --- a/icu4c/source/i18n/numparse_stringsegment.h +++ /dev/null @@ -1,24 +0,0 @@ -// © 2018 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING -#ifndef __NUMPARSE_STRINGSEGMENT_H__ -#define __NUMPARSE_STRINGSEGMENT_H__ - -#include "numparse_types.h" -#include "number_types.h" -#include "unicode/unistr.h" - -U_NAMESPACE_BEGIN -namespace numparse { -namespace impl { - - -} // namespace impl -} // namespace numparse -U_NAMESPACE_END - -#endif //__NUMPARSE_STRINGSEGMENT_H__ -#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/numparse_symbols.cpp b/icu4c/source/i18n/numparse_symbols.cpp index e0daab9374f..bf14c711f05 100644 --- a/icu4c/source/i18n/numparse_symbols.cpp +++ b/icu4c/source/i18n/numparse_symbols.cpp @@ -12,6 +12,7 @@ #include "numparse_types.h" #include "numparse_symbols.h" #include "numparse_utils.h" +#include "string_segment.h" using namespace icu; using namespace icu::numparse; diff --git a/icu4c/source/i18n/numparse_types.h b/icu4c/source/i18n/numparse_types.h index 0fd70faca99..a6bfe68f512 100644 --- a/icu4c/source/i18n/numparse_types.h +++ b/icu4c/source/i18n/numparse_types.h @@ -9,12 +9,13 @@ #include "unicode/uobject.h" #include "number_decimalquantity.h" +#include "string_segment.h" -U_NAMESPACE_BEGIN namespace numparse { +U_NAMESPACE_BEGIN +namespace numparse { namespace impl { // Forward-declarations -class StringSegment; class ParsedNumber; typedef int32_t result_flags_t; @@ -169,115 +170,6 @@ class U_I18N_API ParsedNumber { }; -/** - * A mutable class allowing for a String with a variable offset and length. The charAt, length, and - * subSequence methods all operate relative to the fixed offset into the String. - * - * @author sffc - */ -// Exported as U_I18N_API for tests -class U_I18N_API StringSegment : public UMemory { - public: - StringSegment(const UnicodeString& str, bool ignoreCase); - - int32_t getOffset() const; - - void setOffset(int32_t start); - - /** - * Equivalent to setOffset(getOffset()+delta). - * - *

- * This method is usually called by a Matcher to register that a char was consumed. If the char is - * strong (it usually is, except for things like whitespace), follow this with a call to - * {@link ParsedNumber#setCharsConsumed}. For more information on strong chars, see that method. - */ - void adjustOffset(int32_t delta); - - /** - * Adjusts the offset by the width of the current code point, either 1 or 2 chars. - */ - void adjustOffsetByCodePoint(); - - void setLength(int32_t length); - - void resetLength(); - - int32_t length() const; - - char16_t charAt(int32_t index) const; - - UChar32 codePointAt(int32_t index) const; - - UnicodeString toUnicodeString() const; - - const UnicodeString toTempUnicodeString() const; - - /** - * Returns the first code point in the string segment, or -1 if the string starts with an invalid - * code point. - * - *

- * Important: Most of the time, you should use {@link #matches}, which handles case - * folding logic, instead of this method. - */ - UChar32 getCodePoint() const; - - /** - * Returns true if the first code point of this StringSegment equals the given code point. - * - *

- * This method will perform case folding if case folding is enabled for the parser. - */ - bool startsWith(UChar32 otherCp) const; - - /** - * Returns true if the first code point of this StringSegment is in the given UnicodeSet. - */ - bool startsWith(const UnicodeSet& uniset) const; - - /** - * Returns true if there is at least one code point of overlap between this StringSegment and the - * given UnicodeString. - */ - bool startsWith(const UnicodeString& other) const; - - /** - * Returns the length of the prefix shared by this StringSegment and the given CharSequence. For - * example, if this string segment is "aab", and the char sequence is "aac", this method returns 2, - * since the first 2 characters are the same. - * - *

- * This method only returns offsets along code point boundaries. - * - *

- * This method will perform case folding if case folding was enabled in the constructor. - * - *

- * IMPORTANT: The given UnicodeString must not be empty! It is the caller's responsibility to check. - */ - int32_t getCommonPrefixLength(const UnicodeString& other); - - /** - * Like {@link #getCommonPrefixLength}, but never performs case folding, even if case folding is - * enabled for the parser. - */ - int32_t getCaseSensitivePrefixLength(const UnicodeString& other); - - bool operator==(const UnicodeString& other) const; - - private: - const UnicodeString& fStr; - int32_t fStart; - int32_t fEnd; - bool fFoldCase; - - int32_t getPrefixLengthInternal(const UnicodeString& other, bool foldCase); - - static bool codePointsEqual(UChar32 cp1, UChar32 cp2, bool foldCase); -}; - - /** * The core interface implemented by all matchers used for number parsing. * diff --git a/icu4c/source/i18n/numrange_fluent.cpp b/icu4c/source/i18n/numrange_fluent.cpp index 079d5194b03..a788108ae63 100644 --- a/icu4c/source/i18n/numrange_fluent.cpp +++ b/icu4c/source/i18n/numrange_fluent.cpp @@ -381,7 +381,7 @@ UPRV_FORMATTED_VALUE_SUBCLASS_AUTO_IMPL(FormattedNumberRange) UBool FormattedNumberRange::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const { UPRV_FORMATTED_VALUE_METHOD_GUARD(FALSE) // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool - return fData->getStringRef().nextFieldPosition(fieldPosition, status) ? TRUE : FALSE; + return fData->nextFieldPosition(fieldPosition, status); } void FormattedNumberRange::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const { @@ -392,7 +392,7 @@ void FormattedNumberRange::getAllFieldPositions(FieldPositionIterator& iterator, void FormattedNumberRange::getAllFieldPositionsImpl( FieldPositionIteratorHandler& fpih, UErrorCode& status) const { UPRV_FORMATTED_VALUE_METHOD_GUARD() - fData->getStringRef().getAllFieldPositions(fpih, status); + fData->getAllFieldPositions(fpih, status); } UnicodeString FormattedNumberRange::getFirstDecimal(UErrorCode& status) const { diff --git a/icu4c/source/i18n/numrange_impl.cpp b/icu4c/source/i18n/numrange_impl.cpp index 05eb2b84de3..7d732b31ec1 100644 --- a/icu4c/source/i18n/numrange_impl.cpp +++ b/icu4c/source/i18n/numrange_impl.cpp @@ -397,7 +397,7 @@ void NumberRangeFormatterImpl::formatRange(UFormattedNumberRangeData& data, break; } - NumberStringBuilder& string = data.getStringRef(); + FormattedStringBuilder& string = data.getStringRef(); int32_t lengthPrefix = 0; int32_t length1 = 0; int32_t lengthInfix = 0; diff --git a/icu4c/source/i18n/numrange_impl.h b/icu4c/source/i18n/numrange_impl.h index dc25dd4d67b..f88e3009136 100644 --- a/icu4c/source/i18n/numrange_impl.h +++ b/icu4c/source/i18n/numrange_impl.h @@ -13,7 +13,7 @@ #include "number_types.h" #include "number_decimalquantity.h" #include "number_formatimpl.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" #include "formattedval_impl.h" U_NAMESPACE_BEGIN namespace number { @@ -29,9 +29,9 @@ namespace impl { * Possible magic number: 0x46445200 * Reads in ASCII as "FDR" (FormatteDnumberRange with room at the end) */ -class UFormattedNumberRangeData : public FormattedValueNumberStringBuilderImpl { +class UFormattedNumberRangeData : public FormattedValueStringBuilderImpl { public: - UFormattedNumberRangeData() : FormattedValueNumberStringBuilderImpl(0) {} + UFormattedNumberRangeData() : FormattedValueStringBuilderImpl(0) {} virtual ~UFormattedNumberRangeData(); DecimalQuantity quantity1; diff --git a/icu4c/source/i18n/quantityformatter.cpp b/icu4c/source/i18n/quantityformatter.cpp index 9182f9e7d37..b8c26c4ab5f 100644 --- a/icu4c/source/i18n/quantityformatter.cpp +++ b/icu4c/source/i18n/quantityformatter.cpp @@ -26,7 +26,7 @@ #include "uassert.h" #include "number_decimalquantity.h" #include "number_utypes.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" U_NAMESPACE_BEGIN @@ -180,7 +180,7 @@ void QuantityFormatter::formatAndSelect( double quantity, const NumberFormat& fmt, const PluralRules& rules, - number::impl::NumberStringBuilder& output, + FormattedStringBuilder& output, StandardPlural::Form& pluralForm, UErrorCode& status) { UnicodeString pluralKeyword; diff --git a/icu4c/source/i18n/quantityformatter.h b/icu4c/source/i18n/quantityformatter.h index 046eec7509e..daaef4f060a 100644 --- a/icu4c/source/i18n/quantityformatter.h +++ b/icu4c/source/i18n/quantityformatter.h @@ -26,12 +26,7 @@ class PluralRules; class NumberFormat; class Formattable; class FieldPosition; - -namespace number { -namespace impl { -class NumberStringBuilder; -} -} +class FormattedStringBuilder; /** * A plural aware formatter that is good for expressing a single quantity and @@ -129,7 +124,7 @@ public: /** * Formats a quantity and selects its plural form. The output is appended - * to a NumberStringBuilder in order to retain field information. + * to a FormattedStringBuilder in order to retain field information. * * @param quantity The number to format. * @param fmt The formatter to use to format the number. @@ -144,7 +139,7 @@ public: double quantity, const NumberFormat& fmt, const PluralRules& rules, - number::impl::NumberStringBuilder& output, + FormattedStringBuilder& output, StandardPlural::Form& pluralForm, UErrorCode& status); diff --git a/icu4c/source/i18n/reldatefmt.cpp b/icu4c/source/i18n/reldatefmt.cpp index 2b58e47c257..fd54bf0c35a 100644 --- a/icu4c/source/i18n/reldatefmt.cpp +++ b/icu4c/source/i18n/reldatefmt.cpp @@ -43,7 +43,7 @@ #include "standardplural.h" #include "unifiedcache.h" #include "util.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" #include "number_utypes.h" #include "number_modifiers.h" #include "formattedval_impl.h" @@ -725,14 +725,14 @@ const RelativeDateTimeCacheData *LocaleCacheKey::crea static constexpr number::impl::Field kRDTNumericField - = number::impl::NumFieldUtils::compress(); + = StringBuilderFieldUtils::compress(); static constexpr number::impl::Field kRDTLiteralField - = number::impl::NumFieldUtils::compress(); + = StringBuilderFieldUtils::compress(); -class FormattedRelativeDateTimeData : public FormattedValueNumberStringBuilderImpl { +class FormattedRelativeDateTimeData : public FormattedValueStringBuilderImpl { public: - FormattedRelativeDateTimeData() : FormattedValueNumberStringBuilderImpl(kRDTNumericField) {} + FormattedRelativeDateTimeData() : FormattedValueStringBuilderImpl(kRDTNumericField) {} virtual ~FormattedRelativeDateTimeData(); }; diff --git a/icu4c/source/i18n/numparse_stringsegment.cpp b/icu4c/source/i18n/string_segment.cpp similarity index 96% rename from icu4c/source/i18n/numparse_stringsegment.cpp rename to icu4c/source/i18n/string_segment.cpp index 3db4fe618a6..5d19ac57f5e 100644 --- a/icu4c/source/i18n/numparse_stringsegment.cpp +++ b/icu4c/source/i18n/string_segment.cpp @@ -10,14 +10,12 @@ #define UNISTR_FROM_STRING_EXPLICIT #include "numparse_types.h" -#include "numparse_stringsegment.h" +#include "string_segment.h" #include "putilimp.h" #include "unicode/utf16.h" #include "unicode/uniset.h" -using namespace icu; -using namespace icu::numparse; -using namespace icu::numparse::impl; +U_NAMESPACE_BEGIN StringSegment::StringSegment(const UnicodeString& str, bool ignoreCase) @@ -143,4 +141,5 @@ bool StringSegment::operator==(const UnicodeString& other) const { } +U_NAMESPACE_END #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/string_segment.h b/icu4c/source/i18n/string_segment.h new file mode 100644 index 00000000000..b581f7e5759 --- /dev/null +++ b/icu4c/source/i18n/string_segment.h @@ -0,0 +1,134 @@ +// © 2018 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING +#ifndef __NUMPARSE_STRINGSEGMENT_H__ +#define __NUMPARSE_STRINGSEGMENT_H__ + +#include "unicode/unistr.h" +#include "unicode/uniset.h" + +U_NAMESPACE_BEGIN + + +/** + * A mutable UnicodeString wrapper with a variable offset and length and + * support for case folding. The charAt, length, and subSequence methods all + * operate relative to the fixed offset into the UnicodeString. + * + * Intended to be useful for parsing. + * + * CAUTION: Since this class is mutable, it must not be used anywhere that an + * immutable object is required, like in a cache or as the key of a hash map. + * + * @author sffc (Shane Carr) + */ +// Exported as U_I18N_API for tests +class U_I18N_API StringSegment : public UMemory { + public: + StringSegment(const UnicodeString& str, bool ignoreCase); + + int32_t getOffset() const; + + void setOffset(int32_t start); + + /** + * Equivalent to setOffset(getOffset()+delta). + * + *

+ * This method is usually called by a Matcher to register that a char was consumed. If the char is + * strong (it usually is, except for things like whitespace), follow this with a call to + * {@link ParsedNumber#setCharsConsumed}. For more information on strong chars, see that method. + */ + void adjustOffset(int32_t delta); + + /** + * Adjusts the offset by the width of the current code point, either 1 or 2 chars. + */ + void adjustOffsetByCodePoint(); + + void setLength(int32_t length); + + void resetLength(); + + int32_t length() const; + + char16_t charAt(int32_t index) const; + + UChar32 codePointAt(int32_t index) const; + + UnicodeString toUnicodeString() const; + + const UnicodeString toTempUnicodeString() const; + + /** + * Returns the first code point in the string segment, or -1 if the string starts with an invalid + * code point. + * + *

+ * Important: Most of the time, you should use {@link #startsWith}, which handles case + * folding logic, instead of this method. + */ + UChar32 getCodePoint() const; + + /** + * Returns true if the first code point of this StringSegment equals the given code point. + * + *

+ * This method will perform case folding if case folding is enabled for the parser. + */ + bool startsWith(UChar32 otherCp) const; + + /** + * Returns true if the first code point of this StringSegment is in the given UnicodeSet. + */ + bool startsWith(const UnicodeSet& uniset) const; + + /** + * Returns true if there is at least one code point of overlap between this StringSegment and the + * given UnicodeString. + */ + bool startsWith(const UnicodeString& other) const; + + /** + * Returns the length of the prefix shared by this StringSegment and the given UnicodeString. For + * example, if this string segment is "aab", and the char sequence is "aac", this method returns 2, + * since the first 2 characters are the same. + * + *

+ * This method only returns offsets along code point boundaries. + * + *

+ * This method will perform case folding if case folding was enabled in the constructor. + * + *

+ * IMPORTANT: The given UnicodeString must not be empty! It is the caller's responsibility to check. + */ + int32_t getCommonPrefixLength(const UnicodeString& other); + + /** + * Like {@link #getCommonPrefixLength}, but never performs case folding, even if case folding is + * enabled for the parser. + */ + int32_t getCaseSensitivePrefixLength(const UnicodeString& other); + + bool operator==(const UnicodeString& other) const; + + private: + const UnicodeString& fStr; + int32_t fStart; + int32_t fEnd; + bool fFoldCase; + + int32_t getPrefixLengthInternal(const UnicodeString& other, bool foldCase); + + static bool codePointsEqual(UChar32 cp1, UChar32 cp2, bool foldCase); +}; + + +U_NAMESPACE_END + +#endif //__NUMPARSE_STRINGSEGMENT_H__ +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/i18n/unicode/numberformatter.h b/icu4c/source/i18n/unicode/numberformatter.h index 4fd6830518d..5144ea98c6c 100644 --- a/icu4c/source/i18n/unicode/numberformatter.h +++ b/icu4c/source/i18n/unicode/numberformatter.h @@ -85,6 +85,7 @@ U_NAMESPACE_BEGIN // Forward declarations: class IFixedDecimal; class FieldPositionIteratorHandler; +class FormattedStringBuilder; namespace numparse { namespace impl { @@ -142,7 +143,6 @@ class MultiplierProducer; class RoundingImpl; class ScientificHandler; class Modifier; -class NumberStringBuilder; class AffixPatternProvider; class NumberPropertyMapper; struct DecimalFormatProperties; @@ -1343,7 +1343,7 @@ class U_I18N_API Padder : public UMemory { } int32_t padAndApply(const impl::Modifier &mod1, const impl::Modifier &mod2, - impl::NumberStringBuilder &string, int32_t leftIndex, int32_t rightIndex, + FormattedStringBuilder &string, int32_t leftIndex, int32_t rightIndex, UErrorCode &status) const; // To allow MacroProps/MicroProps to initialize empty instances: diff --git a/icu4c/source/test/depstest/dependencies.txt b/icu4c/source/test/depstest/dependencies.txt index 466e6427f95..f7404a0528b 100644 --- a/icu4c/source/test/depstest/dependencies.txt +++ b/icu4c/source/test/depstest/dependencies.txt @@ -932,7 +932,9 @@ group: double_conversion platform group: number_representation - number_decimalquantity.o number_stringbuilder.o numparse_stringsegment.o number_utils.o + number_decimalquantity.o string_segment.o number_utils.o + # TODO(ICU-20429) Move formatted_string_builder to its own unit. + formatted_string_builder.o deps decnumber double_conversion # for trimming whitespace around fields diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in index b4cf918ec48..3a7c75413b4 100644 --- a/icu4c/source/test/intltest/Makefile.in +++ b/icu4c/source/test/intltest/Makefile.in @@ -64,10 +64,10 @@ scientificnumberformattertest.o datadrivennumberformattestsuite.o \ numberformattesttuple.o pluralmaptest.o \ numbertest_affixutils.o numbertest_api.o numbertest_decimalquantity.o \ numbertest_modifiers.o numbertest_patternmodifier.o numbertest_patternstring.o \ -numbertest_stringbuilder.o numbertest_stringsegment.o \ +string_segment_test.o \ numbertest_parse.o numbertest_doubleconversion.o numbertest_skeletons.o \ static_unisets_test.o numfmtdatadriventest.o numbertest_range.o erarulestest.o \ -formattedvaluetest.o +formattedvaluetest.o formatted_string_builder_test.o DEPS = $(OBJECTS:.o=.d) diff --git a/icu4c/source/test/intltest/numbertest_stringbuilder.cpp b/icu4c/source/test/intltest/formatted_string_builder_test.cpp similarity index 81% rename from icu4c/source/test/intltest/numbertest_stringbuilder.cpp rename to icu4c/source/test/intltest/formatted_string_builder_test.cpp index eaf9a7a4d5f..9da402fb2fd 100644 --- a/icu4c/source/test/intltest/numbertest_stringbuilder.cpp +++ b/icu4c/source/test/intltest/formatted_string_builder_test.cpp @@ -6,7 +6,26 @@ #if !UCONFIG_NO_FORMATTING #include "putilimp.h" -#include "numbertest.h" +#include "intltest.h" +#include "formatted_string_builder.h" +#include "formattedval_impl.h" + + +class FormattedStringBuilderTest : public IntlTest { + public: + void testInsertAppendUnicodeString(); + void testSplice(); + void testInsertAppendCodePoint(); + void testCopy(); + void testFields(); + void testUnlimitedCapacity(); + void testCodePoints(); + + void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0); + + private: + void assertEqualsImpl(const UnicodeString &a, const FormattedStringBuilder &b); +}; static const char16_t *EXAMPLE_STRINGS[] = { u"", @@ -17,9 +36,9 @@ static const char16_t *EXAMPLE_STRINGS[] = { u"with combining characters like 🇦🇧🇨🇩", u"A very very very very very very very very very very long string to force heap"}; -void NumberStringBuilderTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) { +void FormattedStringBuilderTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) { if (exec) { - logln("TestSuite NumberStringBuilderTest: "); + logln("TestSuite FormattedStringBuilderTest: "); } TESTCASE_AUTO_BEGIN; TESTCASE_AUTO(testInsertAppendUnicodeString); @@ -32,14 +51,14 @@ void NumberStringBuilderTest::runIndexedTest(int32_t index, UBool exec, const ch TESTCASE_AUTO_END; } -void NumberStringBuilderTest::testInsertAppendUnicodeString() { +void FormattedStringBuilderTest::testInsertAppendUnicodeString() { UErrorCode status = U_ZERO_ERROR; UnicodeString sb1; - NumberStringBuilder sb2; + FormattedStringBuilder sb2; for (const char16_t* strPtr : EXAMPLE_STRINGS) { UnicodeString str(strPtr); - NumberStringBuilder sb3; + FormattedStringBuilder sb3; sb1.append(str); // Note: UNUM_FIELD_COUNT is like passing null in Java sb2.append(str, UNUM_FIELD_COUNT, status); @@ -50,7 +69,7 @@ void NumberStringBuilderTest::testInsertAppendUnicodeString() { assertEqualsImpl(str, sb3); UnicodeString sb4; - NumberStringBuilder sb5; + FormattedStringBuilder sb5; sb4.append(u"😇"); sb4.append(str); sb4.append(u"xx"); @@ -68,7 +87,7 @@ void NumberStringBuilderTest::testInsertAppendUnicodeString() { assertEqualsImpl(sb4, sb5); UnicodeString sb4cp(sb4); - NumberStringBuilder sb5cp(sb5); + FormattedStringBuilder sb5cp(sb5); sb4.append(sb4cp); sb5.append(sb5cp, status); assertSuccess("Appending again to sb5", status); @@ -76,7 +95,7 @@ void NumberStringBuilderTest::testInsertAppendUnicodeString() { } } -void NumberStringBuilderTest::testSplice() { +void FormattedStringBuilderTest::testSplice() { static const struct TestCase { const char16_t* input; const int32_t startThis; @@ -94,7 +113,7 @@ void NumberStringBuilderTest::testSplice() { UErrorCode status = U_ZERO_ERROR; UnicodeString sb1; - NumberStringBuilder sb2; + FormattedStringBuilder sb2; for (auto cas : cases) { for (const char16_t* replacementPtr : EXAMPLE_STRINGS) { UnicodeString replacement(replacementPtr); @@ -125,14 +144,14 @@ void NumberStringBuilderTest::testSplice() { } } -void NumberStringBuilderTest::testInsertAppendCodePoint() { +void FormattedStringBuilderTest::testInsertAppendCodePoint() { static const UChar32 cases[] = { 0, 1, 60, 127, 128, 0x7fff, 0x8000, 0xffff, 0x10000, 0x1f000, 0x10ffff}; UErrorCode status = U_ZERO_ERROR; UnicodeString sb1; - NumberStringBuilder sb2; + FormattedStringBuilder sb2; for (UChar32 cas : cases) { - NumberStringBuilder sb3; + FormattedStringBuilder sb3; sb1.append(cas); sb2.appendCodePoint(cas, UNUM_FIELD_COUNT, status); assertSuccess("Appending to sb2", status); @@ -147,7 +166,7 @@ void NumberStringBuilderTest::testInsertAppendCodePoint() { sb3.charAt(0)); UnicodeString sb4; - NumberStringBuilder sb5; + FormattedStringBuilder sb5; sb4.append(u"😇xx"); sb4.insert(2, cas); sb5.append(u"😇xx", UNUM_FIELD_COUNT, status); @@ -158,13 +177,13 @@ void NumberStringBuilderTest::testInsertAppendCodePoint() { } } -void NumberStringBuilderTest::testCopy() { +void FormattedStringBuilderTest::testCopy() { UErrorCode status = U_ZERO_ERROR; for (UnicodeString str : EXAMPLE_STRINGS) { - NumberStringBuilder sb1; + FormattedStringBuilder sb1; sb1.append(str, UNUM_FIELD_COUNT, status); assertSuccess("Appending to sb1 first time", status); - NumberStringBuilder sb2(sb1); + FormattedStringBuilder sb2(sb1); assertTrue("Content should equal itself", sb1.contentEquals(sb2)); sb1.append("12345", UNUM_FIELD_COUNT, status); @@ -173,25 +192,28 @@ void NumberStringBuilderTest::testCopy() { } } -void NumberStringBuilderTest::testFields() { +void FormattedStringBuilderTest::testFields() { UErrorCode status = U_ZERO_ERROR; // Note: This is a C++11 for loop that calls the UnicodeString constructor on each iteration. for (UnicodeString str : EXAMPLE_STRINGS) { - NumberStringBuilder sb; + FormattedValueStringBuilderImpl sbi(0); + FormattedStringBuilder& sb = sbi.getStringRef(); sb.append(str, UNUM_FIELD_COUNT, status); assertSuccess("Appending to sb", status); sb.append(str, UNUM_CURRENCY_FIELD, status); assertSuccess("Appending to sb", status); assertEquals("Reference string copied twice", str.length() * 2, sb.length()); for (int32_t i = 0; i < str.length(); i++) { - assertEquals("Null field first", (Field) UNUM_FIELD_COUNT, sb.fieldAt(i)); - assertEquals("Currency field second", (Field) UNUM_CURRENCY_FIELD, sb.fieldAt(i + str.length())); + assertEquals("Null field first", + (FormattedStringBuilder::Field) UNUM_FIELD_COUNT, sb.fieldAt(i)); + assertEquals("Currency field second", + (FormattedStringBuilder::Field) UNUM_CURRENCY_FIELD, sb.fieldAt(i + str.length())); } // Very basic FieldPosition test. More robust tests happen in NumberFormatTest. // Let NumberFormatTest also take care of FieldPositionIterator material. FieldPosition fp(UNUM_CURRENCY_FIELD); - sb.nextFieldPosition(fp, status); + sbi.nextFieldPosition(fp, status); assertSuccess("Populating the FieldPosition", status); assertEquals("Currency start position", str.length(), fp.getBeginIndex()); assertEquals("Currency end position", str.length() * 2, fp.getEndIndex()); @@ -200,17 +222,17 @@ void NumberStringBuilderTest::testFields() { sb.insertCodePoint(2, 100, UNUM_INTEGER_FIELD, status); assertSuccess("Inserting code point into sb", status); assertEquals("New length", str.length() * 2 + 1, sb.length()); - assertEquals("Integer field", (Field) UNUM_INTEGER_FIELD, sb.fieldAt(2)); + assertEquals("Integer field", (FormattedStringBuilder::Field) UNUM_INTEGER_FIELD, sb.fieldAt(2)); } - NumberStringBuilder old(sb); + FormattedStringBuilder old(sb); sb.append(old, status); assertSuccess("Appending to myself", status); int32_t numNull = 0; int32_t numCurr = 0; int32_t numInt = 0; for (int32_t i = 0; i < sb.length(); i++) { - Field field = sb.fieldAt(i); + FormattedStringBuilder::Field field = sb.fieldAt(i); assertEquals("Field should equal location in old", old.fieldAt(i % old.length()), field); if (field == UNUM_FIELD_COUNT) { numNull++; @@ -228,9 +250,9 @@ void NumberStringBuilderTest::testFields() { } } -void NumberStringBuilderTest::testUnlimitedCapacity() { +void FormattedStringBuilderTest::testUnlimitedCapacity() { UErrorCode status = U_ZERO_ERROR; - NumberStringBuilder builder; + FormattedStringBuilder builder; // The builder should never fail upon repeated appends. for (int i = 0; i < 1000; i++) { UnicodeString message("Iteration #"); @@ -242,9 +264,9 @@ void NumberStringBuilderTest::testUnlimitedCapacity() { } } -void NumberStringBuilderTest::testCodePoints() { +void FormattedStringBuilderTest::testCodePoints() { UErrorCode status = U_ZERO_ERROR; - NumberStringBuilder nsb; + FormattedStringBuilder nsb; assertEquals("First is -1 on empty string", -1, nsb.getFirstCodePoint()); assertEquals("Last is -1 on empty string", -1, nsb.getLastCodePoint()); assertEquals("Length is 0 on empty string", 0, nsb.codePointCount()); @@ -268,7 +290,7 @@ void NumberStringBuilderTest::testCodePoints() { assertEquals("Code point count is 2", 2, nsb.codePointCount()); } -void NumberStringBuilderTest::assertEqualsImpl(const UnicodeString &a, const NumberStringBuilder &b) { +void FormattedStringBuilderTest::assertEqualsImpl(const UnicodeString &a, const FormattedStringBuilder &b) { // TODO: Why won't this compile without the IntlTest:: qualifier? IntlTest::assertEquals("Lengths should be the same", a.length(), b.length()); IntlTest::assertEquals("Code point counts should be the same", a.countChar32(), b.codePointCount()); @@ -285,4 +307,9 @@ void NumberStringBuilderTest::assertEqualsImpl(const UnicodeString &a, const Num } } + +extern IntlTest *createFormattedStringBuilderTest() { + return new FormattedStringBuilderTest(); +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/intltest.vcxproj b/icu4c/source/test/intltest/intltest.vcxproj index 5e82ef3bdc7..2129cfbd6b1 100644 --- a/icu4c/source/test/intltest/intltest.vcxproj +++ b/icu4c/source/test/intltest/intltest.vcxproj @@ -232,6 +232,7 @@ + @@ -251,8 +252,7 @@ - - + diff --git a/icu4c/source/test/intltest/intltest.vcxproj.filters b/icu4c/source/test/intltest/intltest.vcxproj.filters index bed26bcbbde..945f73729c1 100644 --- a/icu4c/source/test/intltest/intltest.vcxproj.filters +++ b/icu4c/source/test/intltest/intltest.vcxproj.filters @@ -217,6 +217,9 @@ formatting + + formatting + formatting @@ -274,10 +277,7 @@ formatting - - formatting - - + formatting diff --git a/icu4c/source/test/intltest/itformat.cpp b/icu4c/source/test/intltest/itformat.cpp index 1c993fc2a87..870728a9a0a 100644 --- a/icu4c/source/test/intltest/itformat.cpp +++ b/icu4c/source/test/intltest/itformat.cpp @@ -72,6 +72,8 @@ extern IntlTest *createMeasureFormatTest(); extern IntlTest *createNumberFormatSpecificationTest(); extern IntlTest *createScientificNumberFormatterTest(); extern IntlTest *createFormattedValueTest(); +extern IntlTest *createFormattedStringBuilderTest(); +extern IntlTest *createStringSegmentTest(); #define TESTCLASS(id, TestClass) \ @@ -227,6 +229,24 @@ void IntlTestFormat::runIndexedTest( int32_t index, UBool exec, const char* &nam callTest(*test, par); } break; + case 54: + name = "FormattedStringBuilderTest"; + if (exec) { + logln("FormattedStringBuilderTest test---"); + logln((UnicodeString)""); + LocalPointer test(createFormattedStringBuilderTest()); + callTest(*test, par); + } + break; + case 55: + name = "StringSegmentTest"; + if (exec) { + logln("StringSegmentTest test---"); + logln((UnicodeString)""); + LocalPointer test(createStringSegmentTest()); + callTest(*test, par); + } + break; default: name = ""; break; //needed to end loop } if (exec) { diff --git a/icu4c/source/test/intltest/numbertest.h b/icu4c/source/test/intltest/numbertest.h index 1ea3ee8aea5..d139bc0c388 100644 --- a/icu4c/source/test/intltest/numbertest.h +++ b/icu4c/source/test/intltest/numbertest.h @@ -6,11 +6,11 @@ #if !UCONFIG_NO_FORMATTING #pragma once -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" #include "intltest.h" #include "itformat.h" #include "number_affixutils.h" -#include "numparse_stringsegment.h" +#include "string_segment.h" #include "numrange_impl.h" #include "unicode/locid.h" #include "unicode/numberformatter.h" @@ -174,7 +174,7 @@ class ModifiersTest : public IntlTest { UnicodeString expectedChars, UnicodeString expectedFields, UErrorCode &status); - void assertModifierEquals(const Modifier &mod, NumberStringBuilder &sb, int32_t expectedPrefixLength, + void assertModifierEquals(const Modifier &mod, FormattedStringBuilder &sb, int32_t expectedPrefixLength, bool expectedStrong, UnicodeString expectedChars, UnicodeString expectedFields, UErrorCode &status); }; @@ -204,33 +204,6 @@ class PatternStringTest : public IntlTest { private: }; -class NumberStringBuilderTest : public IntlTest { - public: - void testInsertAppendUnicodeString(); - void testSplice(); - void testInsertAppendCodePoint(); - void testCopy(); - void testFields(); - void testUnlimitedCapacity(); - void testCodePoints(); - - void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0); - - private: - void assertEqualsImpl(const UnicodeString &a, const NumberStringBuilder &b); -}; - -class StringSegmentTest : public IntlTest { - public: - void testOffset(); - void testLength(); - void testCharAt(); - void testGetCodePoint(); - void testCommonPrefixLength(); - - void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0); -}; - class NumberParserTest : public IntlTest { public: void testBasic(); @@ -340,12 +313,10 @@ class NumberTest : public IntlTest { TESTCLASS(3, ModifiersTest); TESTCLASS(4, PatternModifierTest); TESTCLASS(5, PatternStringTest); - TESTCLASS(6, NumberStringBuilderTest); - TESTCLASS(7, DoubleConversionTest); - TESTCLASS(8, StringSegmentTest); - TESTCLASS(9, NumberParserTest); - TESTCLASS(10, NumberSkeletonTest); - TESTCLASS(11, NumberRangeFormatterTest); + TESTCLASS(6, DoubleConversionTest); + TESTCLASS(7, NumberParserTest); + TESTCLASS(8, NumberSkeletonTest); + TESTCLASS(9, NumberRangeFormatterTest); default: name = ""; break; // needed to end loop } } diff --git a/icu4c/source/test/intltest/numbertest_affixutils.cpp b/icu4c/source/test/intltest/numbertest_affixutils.cpp index eefded648ab..7f29ad19903 100644 --- a/icu4c/source/test/intltest/numbertest_affixutils.cpp +++ b/icu4c/source/test/intltest/numbertest_affixutils.cpp @@ -217,7 +217,7 @@ void AffixUtilsTest::testUnescapeWithSymbolProvider() { NumericSymbolProvider provider; UErrorCode status = U_ZERO_ERROR; - NumberStringBuilder sb; + FormattedStringBuilder sb; for (auto& cas : cases) { UnicodeString input(cas[0]); UnicodeString expected(cas[1]); @@ -239,7 +239,7 @@ void AffixUtilsTest::testUnescapeWithSymbolProvider() { UnicodeString AffixUtilsTest::unescapeWithDefaults(const SymbolProvider &defaultProvider, UnicodeString input, UErrorCode &status) { - NumberStringBuilder nsb; + FormattedStringBuilder nsb; int32_t length = AffixUtils::unescape(input, nsb, 0, defaultProvider, UNUM_FIELD_COUNT, status); assertEquals("Return value of unescape", nsb.length(), length); return nsb.toUnicodeString(); diff --git a/icu4c/source/test/intltest/numbertest_modifiers.cpp b/icu4c/source/test/intltest/numbertest_modifiers.cpp index 52f4f494751..90144ad1c64 100644 --- a/icu4c/source/test/intltest/numbertest_modifiers.cpp +++ b/icu4c/source/test/intltest/numbertest_modifiers.cpp @@ -7,7 +7,7 @@ #include "putilimp.h" #include "intltest.h" -#include "number_stringbuilder.h" +#include "formatted_string_builder.h" #include "number_modifiers.h" #include "numbertest.h" @@ -36,8 +36,8 @@ void ModifiersTest::testConstantAffixModifier() { void ModifiersTest::testConstantMultiFieldModifier() { UErrorCode status = U_ZERO_ERROR; - NumberStringBuilder prefix; - NumberStringBuilder suffix; + FormattedStringBuilder prefix; + FormattedStringBuilder suffix; ConstantMultiFieldModifier mod1(prefix, suffix, false, true); assertModifierEquals(mod1, 0, true, u"|", u"n", status); assertSuccess("Spot 1", status); @@ -87,7 +87,7 @@ void ModifiersTest::testSimpleModifier() { // Test strange insertion positions for (int32_t j = 0; j < NUM_OUTPUTS; j++) { - NumberStringBuilder output; + FormattedStringBuilder output; output.append(outputs[j].baseString, UNUM_FIELD_COUNT, status); mod.apply(output, outputs[j].leftIndex, outputs[j].rightIndex, status); UnicodeString expected = expecteds[j][i]; @@ -105,8 +105,8 @@ void ModifiersTest::testCurrencySpacingEnabledModifier() { return; } - NumberStringBuilder prefix; - NumberStringBuilder suffix; + FormattedStringBuilder prefix; + FormattedStringBuilder suffix; CurrencySpacingEnabledModifier mod1(prefix, suffix, false, true, symbols, status); assertSuccess("Spot 2", status); assertModifierEquals(mod1, 0, true, u"|", u"n", status); @@ -120,15 +120,15 @@ void ModifiersTest::testCurrencySpacingEnabledModifier() { assertSuccess("Spot 6", status); // Test the default currency spacing rules - NumberStringBuilder sb; + FormattedStringBuilder sb; sb.append("123", UNUM_INTEGER_FIELD, status); assertSuccess("Spot 7", status); - NumberStringBuilder sb1(sb); + FormattedStringBuilder sb1(sb); assertModifierEquals(mod2, sb1, 3, true, u"USD\u00A0123", u"$$$niii", status); assertSuccess("Spot 8", status); // Compare with the unsafe code path - NumberStringBuilder sb2(sb); + FormattedStringBuilder sb2(sb); sb2.insert(0, "USD", UNUM_CURRENCY_FIELD, status); assertSuccess("Spot 9", status); CurrencySpacingEnabledModifier::applyCurrencySpacing(sb2, 0, 3, 6, 0, symbols, status); @@ -149,14 +149,14 @@ void ModifiersTest::testCurrencySpacingEnabledModifier() { void ModifiersTest::assertModifierEquals(const Modifier &mod, int32_t expectedPrefixLength, bool expectedStrong, UnicodeString expectedChars, UnicodeString expectedFields, UErrorCode &status) { - NumberStringBuilder sb; + FormattedStringBuilder sb; sb.appendCodePoint('|', UNUM_FIELD_COUNT, status); assertModifierEquals( mod, sb, expectedPrefixLength, expectedStrong, expectedChars, expectedFields, status); } -void ModifiersTest::assertModifierEquals(const Modifier &mod, NumberStringBuilder &sb, +void ModifiersTest::assertModifierEquals(const Modifier &mod, FormattedStringBuilder &sb, int32_t expectedPrefixLength, bool expectedStrong, UnicodeString expectedChars, UnicodeString expectedFields, UErrorCode &status) { @@ -171,7 +171,7 @@ void ModifiersTest::assertModifierEquals(const Modifier &mod, NumberStringBuilde } UnicodeString debugString; - debugString.append(u"apply(nsb1, 0, 0, status); assertSuccess("Spot 3", status); - NumberStringBuilder nsb2; + FormattedStringBuilder nsb2; MicroProps micros2; LocalPointer immutable(mod.createImmutable(status)); immutable->applyToMicros(micros2, fq, status); micros2.modMiddle->apply(nsb2, 0, 0, status); assertSuccess("Spot 4", status); - NumberStringBuilder nsb3; + FormattedStringBuilder nsb3; MicroProps micros3; mod.addToChain(µs3); mod.setPatternAttributes(UNUM_SIGN_ALWAYS, false); @@ -168,14 +168,14 @@ void PatternModifierTest::testMutableEqualsImmutable() { } UnicodeString PatternModifierTest::getPrefix(const MutablePatternModifier &mod, UErrorCode &status) { - NumberStringBuilder nsb; + FormattedStringBuilder nsb; mod.apply(nsb, 0, 0, status); int32_t prefixLength = mod.getPrefixLength(); return UnicodeString(nsb.toUnicodeString(), 0, prefixLength); } UnicodeString PatternModifierTest::getSuffix(const MutablePatternModifier &mod, UErrorCode &status) { - NumberStringBuilder nsb; + FormattedStringBuilder nsb; mod.apply(nsb, 0, 0, status); int32_t prefixLength = mod.getPrefixLength(); return UnicodeString(nsb.toUnicodeString(), prefixLength, nsb.length() - prefixLength); diff --git a/icu4c/source/test/intltest/numbertest_stringsegment.cpp b/icu4c/source/test/intltest/string_segment_test.cpp similarity index 91% rename from icu4c/source/test/intltest/numbertest_stringsegment.cpp rename to icu4c/source/test/intltest/string_segment_test.cpp index dc5774fd3a7..0548d9ad928 100644 --- a/icu4c/source/test/intltest/numbertest_stringsegment.cpp +++ b/icu4c/source/test/intltest/string_segment_test.cpp @@ -5,8 +5,19 @@ #if !UCONFIG_NO_FORMATTING -#include "numbertest.h" -#include "numparse_stringsegment.h" +#include "string_segment.h" +#include "intltest.h" + +class StringSegmentTest : public IntlTest { + public: + void testOffset(); + void testLength(); + void testCharAt(); + void testGetCodePoint(); + void testCommonPrefixLength(); + + void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0); +}; static const char16_t* SAMPLE_STRING = u"📻 radio 📻"; @@ -101,4 +112,9 @@ void StringSegmentTest::testCommonPrefixLength() { assertEquals("", 0, segment.getCommonPrefixLength(u"foo")); } + +extern IntlTest *createStringSegmentTest() { + return new StringSegmentTest(); +} + #endif diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java similarity index 64% rename from icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java rename to icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java index 60a5467022f..ca3cb187bda 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java @@ -1,22 +1,17 @@ // © 2017 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number; +package com.ibm.icu.impl; -import java.text.AttributedCharacterIterator; -import java.text.AttributedString; -import java.text.FieldPosition; import java.text.Format.Field; import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import com.ibm.icu.impl.StaticUnicodeSets; -import com.ibm.icu.text.ConstrainedFieldPosition; +// NumberFormat is imported only for the toDebugString() implementation. import com.ibm.icu.text.NumberFormat; -import com.ibm.icu.text.UnicodeSet; /** - * A StringBuilder optimized for number formatting. It implements the following key features beyond a + * A StringBuilder optimized for formatting. It implements the following key features beyond a * normal JDK StringBuilder: * *

    @@ -24,33 +19,37 @@ import com.ibm.icu.text.UnicodeSet; *
  1. Keeps tracks of Fields in an efficient manner. *
  2. String operations are fast-pathed to code point operations when possible. *
+ * + * See also FormattedValueStringBuilderImpl. + * + * @author sffc (Shane Carr) */ -public class NumberStringBuilder implements CharSequence { +public class FormattedStringBuilder implements CharSequence { - /** A constant, empty NumberStringBuilder. Do NOT call mutative operations on this. */ - public static final NumberStringBuilder EMPTY = new NumberStringBuilder(); + /** A constant, empty FormattedStringBuilder. Do NOT call mutative operations on this. */ + public static final FormattedStringBuilder EMPTY = new FormattedStringBuilder(); - private char[] chars; - private Field[] fields; - private int zero; - private int length; + char[] chars; + Field[] fields; + int zero; + int length; - public NumberStringBuilder() { + public FormattedStringBuilder() { this(40); } - public NumberStringBuilder(int capacity) { + public FormattedStringBuilder(int capacity) { chars = new char[capacity]; fields = new Field[capacity]; zero = capacity / 2; length = 0; } - public NumberStringBuilder(NumberStringBuilder source) { + public FormattedStringBuilder(FormattedStringBuilder source) { copyFrom(source); } - public void copyFrom(NumberStringBuilder source) { + public void copyFrom(FormattedStringBuilder source) { chars = Arrays.copyOf(source.chars, source.chars.length); fields = Arrays.copyOf(source.fields, source.fields.length); zero = source.zero; @@ -101,7 +100,7 @@ public class NumberStringBuilder implements CharSequence { return Character.codePointBefore(chars, zero + index, zero); } - public NumberStringBuilder clear() { + public FormattedStringBuilder clear() { zero = getCapacity() / 2; length = 0; return this; @@ -237,20 +236,20 @@ public class NumberStringBuilder implements CharSequence { } /** - * Appends the contents of another {@link NumberStringBuilder} to the end of this instance. + * Appends the contents of another {@link FormattedStringBuilder} to the end of this instance. * - * @return The number of chars added, which is the length of the other {@link NumberStringBuilder}. + * @return The number of chars added, which is the length of the other {@link FormattedStringBuilder}. */ - public int append(NumberStringBuilder other) { + public int append(FormattedStringBuilder other) { return insert(length, other); } /** - * Inserts the contents of another {@link NumberStringBuilder} into this instance at the given index. + * Inserts the contents of another {@link FormattedStringBuilder} into this instance at the given index. * - * @return The number of chars added, which is the length of the other {@link NumberStringBuilder}. + * @return The number of chars added, which is the length of the other {@link FormattedStringBuilder}. */ - public int insert(int index, NumberStringBuilder other) { + public int insert(int index, FormattedStringBuilder other) { if (this == other) { throw new IllegalArgumentException("Cannot call insert/append on myself"); } @@ -365,14 +364,14 @@ public class NumberStringBuilder implements CharSequence { return chars.length; } - /** Note: this returns a NumberStringBuilder. Do not return publicly. */ + /** Note: this returns a FormattedStringBuilder. Do not return publicly. */ @Override @Deprecated public CharSequence subSequence(int start, int end) { assert start >= 0; assert end <= length; assert end >= start; - NumberStringBuilder other = new NumberStringBuilder(this); + FormattedStringBuilder other = new FormattedStringBuilder(this); other.zero = zero + start; other.length = end - start; return other; @@ -420,20 +419,22 @@ public class NumberStringBuilder implements CharSequence { * *

* For example, if the string is "-12.345", the debug string will be something like - * "<NumberStringBuilder [-123.45] [-iii.ff]>" + * "<FormattedStringBuilder [-123.45] [-iii.ff]>" * * @return A string for debugging purposes. */ public String toDebugString() { StringBuilder sb = new StringBuilder(); - sb.append(""); @@ -475,7 +476,7 @@ public class NumberStringBuilder implements CharSequence { * The instance to compare. * @return Whether the contents of this instance is currently equal to the given instance. */ - public boolean contentEquals(NumberStringBuilder other) { + public boolean contentEquals(FormattedStringBuilder other) { if (length != other.length) return false; for (int i = 0; i < length; i++) { @@ -495,170 +496,4 @@ public class NumberStringBuilder implements CharSequence { public boolean equals(Object other) { throw new UnsupportedOperationException("Don't call #hashCode() or #equals() on a mutable."); } - - public boolean nextFieldPosition(FieldPosition fp) { - java.text.Format.Field rawField = fp.getFieldAttribute(); - - if (rawField == null) { - // Backwards compatibility: read from fp.getField() - if (fp.getField() == NumberFormat.INTEGER_FIELD) { - rawField = NumberFormat.Field.INTEGER; - } else if (fp.getField() == NumberFormat.FRACTION_FIELD) { - rawField = NumberFormat.Field.FRACTION; - } else { - // No field is set - return false; - } - } - - if (!(rawField instanceof NumberFormat.Field)) { - throw new IllegalArgumentException( - "You must pass an instance of com.ibm.icu.text.NumberFormat.Field as your FieldPosition attribute. You passed: " - + rawField.getClass().toString()); - } - - ConstrainedFieldPosition cfpos = new ConstrainedFieldPosition(); - cfpos.constrainField(rawField); - cfpos.setState(rawField, null, fp.getBeginIndex(), fp.getEndIndex()); - if (nextPosition(cfpos, null)) { - fp.setBeginIndex(cfpos.getStart()); - fp.setEndIndex(cfpos.getLimit()); - return true; - } - - // Special case: fraction should start after integer if fraction is not present - if (rawField == NumberFormat.Field.FRACTION && fp.getEndIndex() == 0) { - boolean inside = false; - int i = zero; - for (; i < zero + length; i++) { - if (isIntOrGroup(fields[i]) || fields[i] == NumberFormat.Field.DECIMAL_SEPARATOR) { - inside = true; - } else if (inside) { - break; - } - } - fp.setBeginIndex(i - zero); - fp.setEndIndex(i - zero); - } - - return false; - } - - public AttributedCharacterIterator toCharacterIterator(Field numericField) { - ConstrainedFieldPosition cfpos = new ConstrainedFieldPosition(); - AttributedString as = new AttributedString(toString()); - while (this.nextPosition(cfpos, numericField)) { - // Backwards compatibility: field value = field - as.addAttribute(cfpos.getField(), cfpos.getField(), cfpos.getStart(), cfpos.getLimit()); - } - return as.getIterator(); - } - - static class NullField extends Field { - private static final long serialVersionUID = 1L; - static final NullField END = new NullField("end"); - private NullField(String name) { - super(name); - } - } - - /** - * Implementation of nextPosition consistent with the contract of FormattedValue. - * - * @param cfpos - * The argument passed to the public API. - * @param numericField - * Optional. If non-null, apply this field to the entire numeric portion of the string. - * @return See FormattedValue#nextPosition. - */ - public boolean nextPosition(ConstrainedFieldPosition cfpos, Field numericField) { - int fieldStart = -1; - Field currField = null; - for (int i = zero + cfpos.getLimit(); i <= zero + length; i++) { - Field _field = (i < zero + length) ? fields[i] : NullField.END; - // Case 1: currently scanning a field. - if (currField != null) { - if (currField != _field) { - int end = i - zero; - // Grouping separators can be whitespace; don't throw them out! - if (currField != NumberFormat.Field.GROUPING_SEPARATOR) { - end = trimBack(end); - } - if (end <= fieldStart) { - // Entire field position is ignorable; skip. - fieldStart = -1; - currField = null; - i--; // look at this index again - continue; - } - int start = fieldStart; - if (currField != NumberFormat.Field.GROUPING_SEPARATOR) { - start = trimFront(start); - } - cfpos.setState(currField, null, start, end); - return true; - } - continue; - } - // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER. - if (cfpos.matchesField(NumberFormat.Field.INTEGER, null) - && i > zero - // don't return the same field twice in a row: - && i - zero > cfpos.getLimit() - && isIntOrGroup(fields[i - 1]) - && !isIntOrGroup(_field)) { - int j = i - 1; - for (; j >= zero && isIntOrGroup(fields[j]); j--) {} - cfpos.setState(NumberFormat.Field.INTEGER, null, j - zero + 1, i - zero); - return true; - } - // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC. - if (numericField != null - && cfpos.matchesField(numericField, null) - && i > zero - // don't return the same field twice in a row: - && (i - zero > cfpos.getLimit() || cfpos.getField() != numericField) - && isNumericField(fields[i - 1]) - && !isNumericField(_field)) { - int j = i - 1; - for (; j >= zero && isNumericField(fields[j]); j--) {} - cfpos.setState(numericField, null, j - zero + 1, i - zero); - return true; - } - // Special case: skip over INTEGER; will be coalesced later. - if (_field == NumberFormat.Field.INTEGER) { - _field = null; - } - // Case 2: no field starting at this position. - if (_field == null || _field == NullField.END) { - continue; - } - // Case 3: check for field starting at this position - if (cfpos.matchesField(_field, null)) { - fieldStart = i - zero; - currField = _field; - } - } - - assert currField == null; - return false; - } - - private static boolean isIntOrGroup(Field field) { - return field == NumberFormat.Field.INTEGER || field == NumberFormat.Field.GROUPING_SEPARATOR; - } - - private static boolean isNumericField(Field field) { - return field == null || NumberFormat.Field.class.isAssignableFrom(field.getClass()); - } - - private int trimBack(int limit) { - return StaticUnicodeSets.get(StaticUnicodeSets.Key.DEFAULT_IGNORABLES) - .spanBack(this, limit, UnicodeSet.SpanCondition.CONTAINED); - } - - private int trimFront(int start) { - return StaticUnicodeSets.get(StaticUnicodeSets.Key.DEFAULT_IGNORABLES) - .span(this, start, UnicodeSet.SpanCondition.CONTAINED); - } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedValueStringBuilderImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedValueStringBuilderImpl.java new file mode 100644 index 00000000000..8bfe7a5ec5f --- /dev/null +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedValueStringBuilderImpl.java @@ -0,0 +1,193 @@ +// © 2019 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html#License +package com.ibm.icu.impl; + +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.text.FieldPosition; +import java.text.Format.Field; + +import com.ibm.icu.text.ConstrainedFieldPosition; +import com.ibm.icu.text.NumberFormat; +import com.ibm.icu.text.UnicodeSet; + +/** + * Implementation of FormattedValue based on FormattedStringBuilder. + * + * The implementation currently revolves around numbers and number fields. + * However, it can be generalized in the future when there is a need. + * + * In C++, this implements FormattedValue. In Java, it is a stateless + * collection of static functions to avoid having to use nested objects. + * + * @author sffc (Shane Carr) + */ +public class FormattedValueStringBuilderImpl { + + + public static boolean nextFieldPosition(FormattedStringBuilder self, FieldPosition fp) { + java.text.Format.Field rawField = fp.getFieldAttribute(); + + if (rawField == null) { + // Backwards compatibility: read from fp.getField() + if (fp.getField() == NumberFormat.INTEGER_FIELD) { + rawField = NumberFormat.Field.INTEGER; + } else if (fp.getField() == NumberFormat.FRACTION_FIELD) { + rawField = NumberFormat.Field.FRACTION; + } else { + // No field is set + return false; + } + } + + if (!(rawField instanceof NumberFormat.Field)) { + throw new IllegalArgumentException( + "You must pass an instance of com.ibm.icu.text.NumberFormat.Field as your FieldPosition attribute. You passed: " + + rawField.getClass().toString()); + } + + ConstrainedFieldPosition cfpos = new ConstrainedFieldPosition(); + cfpos.constrainField(rawField); + cfpos.setState(rawField, null, fp.getBeginIndex(), fp.getEndIndex()); + if (nextPosition(self, cfpos, null)) { + fp.setBeginIndex(cfpos.getStart()); + fp.setEndIndex(cfpos.getLimit()); + return true; + } + + // Special case: fraction should start after integer if fraction is not present + if (rawField == NumberFormat.Field.FRACTION && fp.getEndIndex() == 0) { + boolean inside = false; + int i = self.zero; + for (; i < self.zero + self.length; i++) { + if (isIntOrGroup(self.fields[i]) || self.fields[i] == NumberFormat.Field.DECIMAL_SEPARATOR) { + inside = true; + } else if (inside) { + break; + } + } + fp.setBeginIndex(i - self.zero); + fp.setEndIndex(i - self.zero); + } + + return false; + } + + public static AttributedCharacterIterator toCharacterIterator(FormattedStringBuilder self, Field numericField) { + ConstrainedFieldPosition cfpos = new ConstrainedFieldPosition(); + AttributedString as = new AttributedString(self.toString()); + while (nextPosition(self, cfpos, numericField)) { + // Backwards compatibility: field value = field + as.addAttribute(cfpos.getField(), cfpos.getField(), cfpos.getStart(), cfpos.getLimit()); + } + return as.getIterator(); + } + + static class NullField extends Field { + private static final long serialVersionUID = 1L; + static final NullField END = new NullField("end"); + private NullField(String name) { + super(name); + } + } + + /** + * Implementation of nextPosition consistent with the contract of FormattedValue. + * + * @param cfpos + * The argument passed to the public API. + * @param numericField + * Optional. If non-null, apply this field to the entire numeric portion of the string. + * @return See FormattedValue#nextPosition. + */ + public static boolean nextPosition(FormattedStringBuilder self, ConstrainedFieldPosition cfpos, Field numericField) { + int fieldStart = -1; + Field currField = null; + for (int i = self.zero + cfpos.getLimit(); i <= self.zero + self.length; i++) { + Field _field = (i < self.zero + self.length) ? self.fields[i] : NullField.END; + // Case 1: currently scanning a field. + if (currField != null) { + if (currField != _field) { + int end = i - self.zero; + // Grouping separators can be whitespace; don't throw them out! + if (currField != NumberFormat.Field.GROUPING_SEPARATOR) { + end = trimBack(self, end); + } + if (end <= fieldStart) { + // Entire field position is ignorable; skip. + fieldStart = -1; + currField = null; + i--; // look at this index again + continue; + } + int start = fieldStart; + if (currField != NumberFormat.Field.GROUPING_SEPARATOR) { + start = trimFront(self, start); + } + cfpos.setState(currField, null, start, end); + return true; + } + continue; + } + // Special case: coalesce the INTEGER if we are pointing at the end of the INTEGER. + if (cfpos.matchesField(NumberFormat.Field.INTEGER, null) + && i > self.zero + // don't return the same field twice in a row: + && i - self.zero > cfpos.getLimit() + && isIntOrGroup(self.fields[i - 1]) + && !isIntOrGroup(_field)) { + int j = i - 1; + for (; j >= self.zero && isIntOrGroup(self.fields[j]); j--) {} + cfpos.setState(NumberFormat.Field.INTEGER, null, j - self.zero + 1, i - self.zero); + return true; + } + // Special case: coalesce NUMERIC if we are pointing at the end of the NUMERIC. + if (numericField != null + && cfpos.matchesField(numericField, null) + && i > self.zero + // don't return the same field twice in a row: + && (i - self.zero > cfpos.getLimit() || cfpos.getField() != numericField) + && isNumericField(self.fields[i - 1]) + && !isNumericField(_field)) { + int j = i - 1; + for (; j >= self.zero && isNumericField(self.fields[j]); j--) {} + cfpos.setState(numericField, null, j - self.zero + 1, i - self.zero); + return true; + } + // Special case: skip over INTEGER; will be coalesced later. + if (_field == NumberFormat.Field.INTEGER) { + _field = null; + } + // Case 2: no field starting at this position. + if (_field == null || _field == NullField.END) { + continue; + } + // Case 3: check for field starting at this position + if (cfpos.matchesField(_field, null)) { + fieldStart = i - self.zero; + currField = _field; + } + } + + assert currField == null; + return false; + } + + private static boolean isIntOrGroup(Field field) { + return field == NumberFormat.Field.INTEGER || field == NumberFormat.Field.GROUPING_SEPARATOR; + } + + private static boolean isNumericField(Field field) { + return field == null || NumberFormat.Field.class.isAssignableFrom(field.getClass()); + } + + private static int trimBack(FormattedStringBuilder self, int limit) { + return StaticUnicodeSets.get(StaticUnicodeSets.Key.DEFAULT_IGNORABLES) + .spanBack(self, limit, UnicodeSet.SpanCondition.CONTAINED); + } + + private static int trimFront(FormattedStringBuilder self, int start) { + return StaticUnicodeSets.get(StaticUnicodeSets.Key.DEFAULT_IGNORABLES) + .span(self, start, UnicodeSet.SpanCondition.CONTAINED); + } +} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java index 7ccf083ed61..ce3f6679268 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java @@ -6,14 +6,16 @@ import com.ibm.icu.lang.UCharacter; import com.ibm.icu.text.UnicodeSet; /** - * A mutable String wrapper with a variable offset and length and support for case folding. - *

- * The charAt, length, and subSequence methods all operate relative to the fixed offset into the String. - *

- * CAUTION: Since this class is mutable, it must not be used anywhere that an immutable object is - * required, like in a cache or as the key of a hash map. + * A mutable String wrapper with a variable offset and length and + * support for case folding. The charAt, length, and subSequence methods all + * operate relative to the fixed offset into the String. * - * @author sffc + * Intended to be useful for parsing. + * + * CAUTION: Since this class is mutable, it must not be used anywhere that an + * immutable object is required, like in a cache or as the key of a hash map. + * + * @author sffc (Shane Carr) */ public class StringSegment implements CharSequence { private final String str; diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixUtils.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixUtils.java index efb41bce61a..ca79325259c 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixUtils.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixUtils.java @@ -2,6 +2,7 @@ // License & terms of use: http://www.unicode.org/copyright.html#License package com.ibm.icu.impl.number; +import com.ibm.icu.impl.FormattedStringBuilder; import com.ibm.icu.text.NumberFormat; import com.ibm.icu.text.UnicodeSet; @@ -290,7 +291,7 @@ public class AffixUtils { /** * Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", "‰", and "¤" * with the corresponding symbols provided by the {@link SymbolProvider}, and inserts the result into - * the NumberStringBuilder at the requested location. + * the FormattedStringBuilder at the requested location. * *

* Example input: "'-'¤x"; example output: "-$x" @@ -298,16 +299,16 @@ public class AffixUtils { * @param affixPattern * The original string to be unescaped. * @param output - * The NumberStringBuilder to mutate with the result. + * The FormattedStringBuilder to mutate with the result. * @param position - * The index into the NumberStringBuilder to insert the the string. + * The index into the FormattedStringBuilder to insert the the string. * @param provider * An object to generate locale symbols. * @return The length of the string added to affixPattern. */ public static int unescape( CharSequence affixPattern, - NumberStringBuilder output, + FormattedStringBuilder output, int position, SymbolProvider provider, NumberFormat.Field field) { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ConstantAffixModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ConstantAffixModifier.java index c82d50d7e01..92498513dfa 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ConstantAffixModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ConstantAffixModifier.java @@ -4,6 +4,8 @@ package com.ibm.icu.impl.number; import java.text.Format.Field; +import com.ibm.icu.impl.FormattedStringBuilder; + /** * The canonical implementation of {@link Modifier}, containing a prefix and suffix string. */ @@ -52,7 +54,7 @@ public class ConstantAffixModifier implements Modifier { } @Override - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { + public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) { // Insert the suffix first since inserting the prefix will change the rightIndex int length = output.insert(rightIndex, suffix, field); length += output.insert(leftIndex, prefix, field); diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ConstantMultiFieldModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ConstantMultiFieldModifier.java index 673296005a8..39d57c4618d 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ConstantMultiFieldModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ConstantMultiFieldModifier.java @@ -5,14 +5,16 @@ package com.ibm.icu.impl.number; import java.text.Format.Field; import java.util.Arrays; +import com.ibm.icu.impl.FormattedStringBuilder; + /** * An implementation of {@link Modifier} that allows for multiple types of fields in the same modifier. - * Constructed based on the contents of two {@link NumberStringBuilder} instances (one for the prefix, + * Constructed based on the contents of two {@link FormattedStringBuilder} instances (one for the prefix, * one for the suffix). */ public class ConstantMultiFieldModifier implements Modifier { - // NOTE: In Java, these are stored as array pointers. In C++, the NumberStringBuilder is stored by + // NOTE: In Java, these are stored as array pointers. In C++, the FormattedStringBuilder is stored by // value and is treated internally as immutable. protected final char[] prefixChars; protected final char[] suffixChars; @@ -25,16 +27,16 @@ public class ConstantMultiFieldModifier implements Modifier { private final Parameters parameters; public ConstantMultiFieldModifier( - NumberStringBuilder prefix, - NumberStringBuilder suffix, + FormattedStringBuilder prefix, + FormattedStringBuilder suffix, boolean overwrite, boolean strong) { this(prefix, suffix, overwrite, strong, null); } public ConstantMultiFieldModifier( - NumberStringBuilder prefix, - NumberStringBuilder suffix, + FormattedStringBuilder prefix, + FormattedStringBuilder suffix, boolean overwrite, boolean strong, Parameters parameters) { @@ -48,7 +50,7 @@ public class ConstantMultiFieldModifier implements Modifier { } @Override - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { + public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) { int length = output.insert(leftIndex, prefixChars, prefixFields); if (overwrite) { length += output.splice(leftIndex + length, rightIndex + length, "", 0, 0, null); @@ -109,7 +111,7 @@ public class ConstantMultiFieldModifier implements Modifier { @Override public String toString() { - NumberStringBuilder temp = new NumberStringBuilder(); + FormattedStringBuilder temp = new FormattedStringBuilder(); apply(temp, 0, 0); int prefixLength = getPrefixLength(); return String.format("", diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/CurrencySpacingEnabledModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/CurrencySpacingEnabledModifier.java index 61c04c53a4a..e0f3be3fb05 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/CurrencySpacingEnabledModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/CurrencySpacingEnabledModifier.java @@ -4,6 +4,7 @@ package com.ibm.icu.impl.number; import java.text.Format.Field; +import com.ibm.icu.impl.FormattedStringBuilder; import com.ibm.icu.text.DecimalFormatSymbols; import com.ibm.icu.text.NumberFormat; import com.ibm.icu.text.UnicodeSet; @@ -30,8 +31,8 @@ public class CurrencySpacingEnabledModifier extends ConstantMultiFieldModifier { /** Safe code path */ public CurrencySpacingEnabledModifier( - NumberStringBuilder prefix, - NumberStringBuilder suffix, + FormattedStringBuilder prefix, + FormattedStringBuilder suffix, boolean overwrite, boolean strong, DecimalFormatSymbols symbols) { @@ -73,7 +74,7 @@ public class CurrencySpacingEnabledModifier extends ConstantMultiFieldModifier { /** Safe code path */ @Override - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { + public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) { // Currency spacing logic int length = 0; if (rightIndex - leftIndex > 0 @@ -96,7 +97,7 @@ public class CurrencySpacingEnabledModifier extends ConstantMultiFieldModifier { /** Unsafe code path */ public static int applyCurrencySpacing( - NumberStringBuilder output, + FormattedStringBuilder output, int prefixStart, int prefixLen, int suffixStart, @@ -117,7 +118,7 @@ public class CurrencySpacingEnabledModifier extends ConstantMultiFieldModifier { /** Unsafe code path */ private static int applyCurrencySpacingAffix( - NumberStringBuilder output, + FormattedStringBuilder output, int index, byte affix, DecimalFormatSymbols symbols) { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java index f3f4635fedb..cc56329d1d3 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java @@ -4,6 +4,7 @@ package com.ibm.icu.impl.number; import java.text.Format.Field; +import com.ibm.icu.impl.FormattedStringBuilder; import com.ibm.icu.impl.StandardPlural; /** @@ -29,7 +30,7 @@ public interface Modifier { * number is being formatted. * @return The number of characters (UTF-16 code units) that were added to the string builder. */ - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex); + public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex); /** * Gets the length of the prefix. This information can be used in combination with {@link #apply} to diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java index b1c1e9a0ee7..67f79b9ecc7 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/MutablePatternModifier.java @@ -2,6 +2,7 @@ // License & terms of use: http://www.unicode.org/copyright.html#License package com.ibm.icu.impl.number; +import com.ibm.icu.impl.FormattedStringBuilder; import com.ibm.icu.impl.StandardPlural; import com.ibm.icu.impl.number.AffixUtils.SymbolProvider; import com.ibm.icu.number.NumberFormatter.SignDisplay; @@ -167,8 +168,8 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr * @return An immutable that supports both positive and negative numbers. */ public ImmutablePatternModifier createImmutableAndChain(MicroPropsGenerator parent) { - NumberStringBuilder a = new NumberStringBuilder(); - NumberStringBuilder b = new NumberStringBuilder(); + FormattedStringBuilder a = new FormattedStringBuilder(); + FormattedStringBuilder b = new FormattedStringBuilder(); if (needsPlurals()) { // Slower path when we require the plural keyword. AdoptingModifierStore pm = new AdoptingModifierStore(); @@ -200,15 +201,15 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr * spacing support if required. * * @param a - * A working NumberStringBuilder object; passed from the outside to prevent the need to + * A working FormattedStringBuilder object; passed from the outside to prevent the need to * create many new instances if this method is called in a loop. * @param b - * Another working NumberStringBuilder object. + * Another working FormattedStringBuilder object. * @return The constant modifier object. */ private ConstantMultiFieldModifier createConstantModifier( - NumberStringBuilder a, - NumberStringBuilder b) { + FormattedStringBuilder a, + FormattedStringBuilder b) { insertPrefix(a.clear(), 0); insertSuffix(b.clear(), 0); if (patternInfo.hasCurrencySign()) { @@ -280,7 +281,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr } @Override - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { + public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) { int prefixLen = insertPrefix(output, leftIndex); int suffixLen = insertSuffix(output, rightIndex + prefixLen); // If the pattern had no decimal stem body (like #,##0.00), overwrite the value. @@ -341,13 +342,13 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr return false; } - private int insertPrefix(NumberStringBuilder sb, int position) { + private int insertPrefix(FormattedStringBuilder sb, int position) { prepareAffix(true); int length = AffixUtils.unescape(currentAffix, sb, position, this, field); return length; } - private int insertSuffix(NumberStringBuilder sb, int position) { + private int insertSuffix(FormattedStringBuilder sb, int position) { prepareAffix(false); int length = AffixUtils.unescape(currentAffix, sb, position, this, field); return length; diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Padder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Padder.java index 97a310900da..ccbf871ae99 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Padder.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Padder.java @@ -2,6 +2,8 @@ // License & terms of use: http://www.unicode.org/copyright.html#License package com.ibm.icu.impl.number; +import com.ibm.icu.impl.FormattedStringBuilder; + public class Padder { public static final String FALLBACK_PADDING_STRING = "\u0020"; // i.e. a space @@ -79,7 +81,7 @@ public class Padder { public int padAndApply( Modifier mod1, Modifier mod2, - NumberStringBuilder string, + FormattedStringBuilder string, int leftIndex, int rightIndex) { int modLength = mod1.getCodePointCount() + mod2.getCodePointCount(); @@ -114,7 +116,7 @@ public class Padder { private static int addPaddingHelper( String paddingString, int requiredPadding, - NumberStringBuilder string, + FormattedStringBuilder string, int index) { for (int i = 0; i < requiredPadding; i++) { // TODO: If appending to the end, this will cause actual insertion operations. Improve. diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/SimpleModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/SimpleModifier.java index 26cb275c5f7..6241848a259 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/SimpleModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/SimpleModifier.java @@ -4,6 +4,7 @@ package com.ibm.icu.impl.number; import java.text.Format.Field; +import com.ibm.icu.impl.FormattedStringBuilder; import com.ibm.icu.impl.SimpleFormatterImpl; import com.ibm.icu.impl.number.range.PrefixInfixSuffixLengthHelper; import com.ibm.icu.util.ICUException; @@ -65,7 +66,7 @@ public class SimpleModifier implements Modifier { } @Override - public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) { + public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) { return formatAsPrefixSuffix(output, leftIndex, rightIndex); } @@ -138,7 +139,7 @@ public class SimpleModifier implements Modifier { * @return The number of characters (UTF-16 code points) that were added to the StringBuilder. */ public int formatAsPrefixSuffix( - NumberStringBuilder result, + FormattedStringBuilder result, int startIndex, int endIndex) { if (suffixOffset == -1) { @@ -161,16 +162,16 @@ public class SimpleModifier implements Modifier { /** * TODO: Like above, this belongs with the rest of the SimpleFormatterImpl code. - * I put it here so that the SimpleFormatter uses in NumberStringBuilder are near each other. + * I put it here so that the SimpleFormatter uses in FormattedStringBuilder are near each other. * *

- * Applies the compiled two-argument pattern to the NumberStringBuilder. + * Applies the compiled two-argument pattern to the FormattedStringBuilder. * *

* This method is optimized for the case where the prefix and suffix are often empty, such as * in the range pattern like "{0}-{1}". */ - public static void formatTwoArgPattern(String compiledPattern, NumberStringBuilder result, int index, PrefixInfixSuffixLengthHelper h, + public static void formatTwoArgPattern(String compiledPattern, FormattedStringBuilder result, int index, PrefixInfixSuffixLengthHelper h, Field field) { int argLimit = SimpleFormatterImpl.getArgumentLimit(compiledPattern); if (argLimit != 2) { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumber.java b/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumber.java index 4fda533b434..c6071445c24 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumber.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumber.java @@ -7,9 +7,10 @@ import java.text.AttributedCharacterIterator; import java.text.FieldPosition; import java.util.Arrays; +import com.ibm.icu.impl.FormattedStringBuilder; +import com.ibm.icu.impl.FormattedValueStringBuilderImpl; import com.ibm.icu.impl.Utility; import com.ibm.icu.impl.number.DecimalQuantity; -import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.text.ConstrainedFieldPosition; import com.ibm.icu.text.FormattedValue; import com.ibm.icu.text.PluralRules.IFixedDecimal; @@ -25,10 +26,10 @@ import com.ibm.icu.text.PluralRules.IFixedDecimal; * @see NumberFormatter */ public class FormattedNumber implements FormattedValue { - final NumberStringBuilder string; + final FormattedStringBuilder string; final DecimalQuantity fq; - FormattedNumber(NumberStringBuilder nsb, DecimalQuantity fq) { + FormattedNumber(FormattedStringBuilder nsb, DecimalQuantity fq) { this.string = nsb; this.fq = fq; } @@ -96,7 +97,7 @@ public class FormattedNumber implements FormattedValue { */ @Override public boolean nextPosition(ConstrainedFieldPosition cfpos) { - return string.nextPosition(cfpos, null); + return FormattedValueStringBuilderImpl.nextPosition(string, cfpos, null); } /** @@ -107,7 +108,7 @@ public class FormattedNumber implements FormattedValue { */ @Override public AttributedCharacterIterator toCharacterIterator() { - return string.toCharacterIterator(null); + return FormattedValueStringBuilderImpl.toCharacterIterator(string, null); } /** @@ -145,7 +146,7 @@ public class FormattedNumber implements FormattedValue { */ public boolean nextFieldPosition(FieldPosition fieldPosition) { fq.populateUFieldPosition(fieldPosition); - return string.nextFieldPosition(fieldPosition); + return FormattedValueStringBuilderImpl.nextFieldPosition(string, fieldPosition); } /** @@ -179,7 +180,7 @@ public class FormattedNumber implements FormattedValue { */ @Override public int hashCode() { - // NumberStringBuilder and BigDecimal are mutable, so we can't call + // FormattedStringBuilder and BigDecimal are mutable, so we can't call // #equals() or #hashCode() on them directly. return Arrays.hashCode(string.toCharArray()) ^ Arrays.hashCode(string.toFieldArray()) @@ -200,7 +201,7 @@ public class FormattedNumber implements FormattedValue { return false; if (!(other instanceof FormattedNumber)) return false; - // NumberStringBuilder and BigDecimal are mutable, so we can't call + // FormattedStringBuilder and BigDecimal are mutable, so we can't call // #equals() or #hashCode() on them directly. FormattedNumber _other = (FormattedNumber) other; return Arrays.equals(string.toCharArray(), _other.string.toCharArray()) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumberRange.java b/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumberRange.java index e23a96ded2b..9992a5a015e 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumberRange.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumberRange.java @@ -8,8 +8,9 @@ import java.text.AttributedCharacterIterator; import java.text.FieldPosition; import java.util.Arrays; +import com.ibm.icu.impl.FormattedStringBuilder; +import com.ibm.icu.impl.FormattedValueStringBuilderImpl; import com.ibm.icu.impl.number.DecimalQuantity; -import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.number.NumberRangeFormatter.RangeIdentityResult; import com.ibm.icu.text.ConstrainedFieldPosition; import com.ibm.icu.text.FormattedValue; @@ -27,12 +28,12 @@ import com.ibm.icu.util.ICUUncheckedIOException; * @see NumberRangeFormatter */ public class FormattedNumberRange implements FormattedValue { - final NumberStringBuilder string; + final FormattedStringBuilder string; final DecimalQuantity quantity1; final DecimalQuantity quantity2; final RangeIdentityResult identityResult; - FormattedNumberRange(NumberStringBuilder string, DecimalQuantity quantity1, DecimalQuantity quantity2, + FormattedNumberRange(FormattedStringBuilder string, DecimalQuantity quantity1, DecimalQuantity quantity2, RangeIdentityResult identityResult) { this.string = string; this.quantity1 = quantity1; @@ -109,7 +110,7 @@ public class FormattedNumberRange implements FormattedValue { */ @Override public boolean nextPosition(ConstrainedFieldPosition cfpos) { - return string.nextPosition(cfpos, null); + return FormattedValueStringBuilderImpl.nextPosition(string, cfpos, null); } /** @@ -142,7 +143,7 @@ public class FormattedNumberRange implements FormattedValue { * @see NumberRangeFormatter */ public boolean nextFieldPosition(FieldPosition fieldPosition) { - return string.nextFieldPosition(fieldPosition); + return FormattedValueStringBuilderImpl.nextFieldPosition(string, fieldPosition); } /** @@ -153,7 +154,7 @@ public class FormattedNumberRange implements FormattedValue { */ @Override public AttributedCharacterIterator toCharacterIterator() { - return string.toCharacterIterator(null); + return FormattedValueStringBuilderImpl.toCharacterIterator(string, null); } /** @@ -207,7 +208,7 @@ public class FormattedNumberRange implements FormattedValue { */ @Override public int hashCode() { - // NumberStringBuilder and BigDecimal are mutable, so we can't call + // FormattedStringBuilder and BigDecimal are mutable, so we can't call // #equals() or #hashCode() on them directly. return Arrays.hashCode(string.toCharArray()) ^ Arrays.hashCode(string.toFieldArray()) ^ quantity1.toBigDecimal().hashCode() ^ quantity2.toBigDecimal().hashCode(); @@ -227,7 +228,7 @@ public class FormattedNumberRange implements FormattedValue { return false; if (!(other instanceof FormattedNumberRange)) return false; - // NumberStringBuilder and BigDecimal are mutable, so we can't call + // FormattedStringBuilder and BigDecimal are mutable, so we can't call // #equals() or #hashCode() on them directly. FormattedNumberRange _other = (FormattedNumberRange) other; return Arrays.equals(string.toCharArray(), _other.string.toCharArray()) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/LocalizedNumberFormatter.java b/icu4j/main/classes/core/src/com/ibm/icu/number/LocalizedNumberFormatter.java index d50aeee0dc7..eace5ba3f87 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/number/LocalizedNumberFormatter.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/number/LocalizedNumberFormatter.java @@ -7,12 +7,12 @@ import java.text.Format; import java.util.Objects; import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import com.ibm.icu.impl.FormattedStringBuilder; import com.ibm.icu.impl.StandardPlural; import com.ibm.icu.impl.number.DecimalQuantity; import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD; import com.ibm.icu.impl.number.LocalizedNumberFormatterAsFormat; import com.ibm.icu.impl.number.MacroProps; -import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.math.BigDecimal; import com.ibm.icu.util.CurrencyAmount; import com.ibm.icu.util.Measure; @@ -135,9 +135,9 @@ public class LocalizedNumberFormatter extends NumberFormatterSettings 0 ? 4 : 0, numInt); - NumberStringBuilder sb2 = new NumberStringBuilder(); + FormattedStringBuilder sb2 = new FormattedStringBuilder(); sb2.append(sb); assertTrue(sb.contentEquals(sb2)); assertTrue(sb.contentEquals(sb2.toCharArray(), sb2.toFieldArray())); @@ -217,7 +218,7 @@ public class NumberStringBuilderTest { @Test public void testUnlimitedCapacity() { - NumberStringBuilder builder = new NumberStringBuilder(); + FormattedStringBuilder builder = new FormattedStringBuilder(); // The builder should never fail upon repeated appends. for (int i = 0; i < 1000; i++) { assertEquals(builder.length(), i); @@ -228,7 +229,7 @@ public class NumberStringBuilderTest { @Test public void testCodePoints() { - NumberStringBuilder nsb = new NumberStringBuilder(); + FormattedStringBuilder nsb = new FormattedStringBuilder(); assertEquals("First is -1 on empty string", -1, nsb.getFirstCodePoint()); assertEquals("Last is -1 on empty string", -1, nsb.getLastCodePoint()); assertEquals("Length is 0 on empty string", 0, nsb.codePointCount()); @@ -262,8 +263,8 @@ public class NumberStringBuilderTest { int end = Math.min(12, a.length()); if (start != end) { assertCharSequenceEquals(a.subSequence(start, end), b.subSequence(start, end)); - if (b instanceof NumberStringBuilder) { - NumberStringBuilder bnsb = (NumberStringBuilder) b; + if (b instanceof FormattedStringBuilder) { + FormattedStringBuilder bnsb = (FormattedStringBuilder) b; assertCharSequenceEquals(a.subSequence(start, end), bnsb.subString(start, end)); } } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixUtilsTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixUtilsTest.java index 5c528c620b4..e778170b167 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixUtilsTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixUtilsTest.java @@ -7,9 +7,9 @@ import static org.junit.Assert.fail; import org.junit.Test; +import com.ibm.icu.impl.FormattedStringBuilder; import com.ibm.icu.impl.number.AffixUtils; import com.ibm.icu.impl.number.AffixUtils.SymbolProvider; -import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.text.UnicodeSet; public class AffixUtilsTest { @@ -199,7 +199,7 @@ public class AffixUtilsTest { } }; - NumberStringBuilder sb = new NumberStringBuilder(); + FormattedStringBuilder sb = new FormattedStringBuilder(); for (String[] cas : cases) { String input = cas[0]; String expected = cas[1]; @@ -236,7 +236,7 @@ public class AffixUtilsTest { } private static String unescapeWithDefaults(String input) { - NumberStringBuilder nsb = new NumberStringBuilder(); + FormattedStringBuilder nsb = new FormattedStringBuilder(); int length = AffixUtils.unescape(input, nsb, 0, DEFAULT_SYMBOL_PROVIDER, null); assertEquals("Return value of unescape", nsb.length(), length); return nsb.toString(); diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java index 9271d94b49a..471682a22ab 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java @@ -19,10 +19,10 @@ import com.ibm.icu.dev.impl.number.DecimalQuantity_64BitBCD; import com.ibm.icu.dev.impl.number.DecimalQuantity_ByteArrayBCD; import com.ibm.icu.dev.impl.number.DecimalQuantity_SimpleStorage; import com.ibm.icu.dev.test.TestFmwk; +import com.ibm.icu.impl.FormattedStringBuilder; import com.ibm.icu.impl.number.DecimalFormatProperties; import com.ibm.icu.impl.number.DecimalQuantity; import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD; -import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.impl.number.RoundingUtils; import com.ibm.icu.number.LocalizedNumberFormatter; import com.ibm.icu.number.NumberFormatter; @@ -236,8 +236,8 @@ public class DecimalQuantityTest extends TestFmwk { for (LocalizedNumberFormatter format : formats) { DecimalQuantity q0 = rq0.createCopy(); DecimalQuantity q1 = rq1.createCopy(); - NumberStringBuilder nsb1 = new NumberStringBuilder(); - NumberStringBuilder nsb2 = new NumberStringBuilder(); + FormattedStringBuilder nsb1 = new FormattedStringBuilder(); + FormattedStringBuilder nsb2 = new FormattedStringBuilder(); format.formatImpl(q0, nsb1); format.formatImpl(q1, nsb2); String s1 = nsb1.toString(); diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/ModifierTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/ModifierTest.java index fee90dae83f..2d4d33cb339 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/ModifierTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/ModifierTest.java @@ -7,12 +7,12 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; +import com.ibm.icu.impl.FormattedStringBuilder; import com.ibm.icu.impl.SimpleFormatterImpl; import com.ibm.icu.impl.number.ConstantAffixModifier; import com.ibm.icu.impl.number.ConstantMultiFieldModifier; import com.ibm.icu.impl.number.CurrencySpacingEnabledModifier; import com.ibm.icu.impl.number.Modifier; -import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.impl.number.SimpleModifier; import com.ibm.icu.text.DecimalFormatSymbols; import com.ibm.icu.text.NumberFormat; @@ -29,8 +29,8 @@ public class ModifierTest { @Test public void testConstantMultiFieldModifier() { - NumberStringBuilder prefix = new NumberStringBuilder(); - NumberStringBuilder suffix = new NumberStringBuilder(); + FormattedStringBuilder prefix = new FormattedStringBuilder(); + FormattedStringBuilder suffix = new FormattedStringBuilder(); Modifier mod1 = new ConstantMultiFieldModifier(prefix, suffix, false, true); assertModifierEquals(mod1, 0, true, "|", "n"); @@ -76,7 +76,7 @@ public class ModifierTest { // Test strange insertion positions for (int j = 0; j < outputs.length; j++) { - NumberStringBuilder output = new NumberStringBuilder(); + FormattedStringBuilder output = new FormattedStringBuilder(); output.append((String) outputs[j][0], null); mod.apply(output, (Integer) outputs[j][1], (Integer) outputs[j][2]); String expected = expecteds[j][i]; @@ -89,8 +89,8 @@ public class ModifierTest { @Test public void testCurrencySpacingEnabledModifier() { DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(ULocale.ENGLISH); - NumberStringBuilder prefix = new NumberStringBuilder(); - NumberStringBuilder suffix = new NumberStringBuilder(); + FormattedStringBuilder prefix = new FormattedStringBuilder(); + FormattedStringBuilder suffix = new FormattedStringBuilder(); Modifier mod1 = new CurrencySpacingEnabledModifier(prefix, suffix, false, true, symbols); assertModifierEquals(mod1, 0, true, "|", "n"); @@ -99,13 +99,13 @@ public class ModifierTest { assertModifierEquals(mod2, 3, true, "USD|", "$$$n"); // Test the default currency spacing rules - NumberStringBuilder sb = new NumberStringBuilder(); + FormattedStringBuilder sb = new FormattedStringBuilder(); sb.append("123", NumberFormat.Field.INTEGER); - NumberStringBuilder sb1 = new NumberStringBuilder(sb); + FormattedStringBuilder sb1 = new FormattedStringBuilder(sb); assertModifierEquals(mod2, sb1, 3, true, "USD\u00A0123", "$$$niii"); // Compare with the unsafe code path - NumberStringBuilder sb2 = new NumberStringBuilder(sb); + FormattedStringBuilder sb2 = new FormattedStringBuilder(sb); sb2.insert(0, "USD", NumberFormat.Field.CURRENCY); CurrencySpacingEnabledModifier.applyCurrencySpacing(sb2, 0, 3, 6, 0, symbols); assertTrue(sb1.toDebugString() + " vs " + sb2.toDebugString(), sb1.contentEquals(sb2)); @@ -147,7 +147,7 @@ public class ModifierTest { boolean expectedStrong, String expectedChars, String expectedFields) { - NumberStringBuilder sb = new NumberStringBuilder(); + FormattedStringBuilder sb = new FormattedStringBuilder(); sb.appendCodePoint('|', null); assertModifierEquals(mod, sb, @@ -159,7 +159,7 @@ public class ModifierTest { private void assertModifierEquals( Modifier mod, - NumberStringBuilder sb, + FormattedStringBuilder sb, int expectedPrefixLength, boolean expectedStrong, String expectedChars, @@ -173,7 +173,7 @@ public class ModifierTest { sb.codePointCount() - oldCount, mod.getCodePointCount()); } - assertEquals("", + assertEquals("", sb.toDebugString()); } } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java index 5f2c13c6e07..9f8585be3fa 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/MutablePatternModifierTest.java @@ -8,11 +8,11 @@ import static org.junit.Assert.assertTrue; import org.junit.Test; +import com.ibm.icu.impl.FormattedStringBuilder; import com.ibm.icu.impl.number.DecimalQuantity; import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD; import com.ibm.icu.impl.number.MicroProps; import com.ibm.icu.impl.number.MutablePatternModifier; -import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.impl.number.PatternStringParser; import com.ibm.icu.number.NumberFormatter.SignDisplay; import com.ibm.icu.number.NumberFormatter.UnitWidth; @@ -81,18 +81,18 @@ public class MutablePatternModifierTest { mod.setSymbols(DecimalFormatSymbols.getInstance(ULocale.ENGLISH), null, UnitWidth.SHORT, null); DecimalQuantity fq = new DecimalQuantity_DualStorageBCD(1); - NumberStringBuilder nsb1 = new NumberStringBuilder(); + FormattedStringBuilder nsb1 = new FormattedStringBuilder(); MicroProps micros1 = new MicroProps(false); mod.addToChain(micros1); mod.processQuantity(fq); micros1.modMiddle.apply(nsb1, 0, 0); - NumberStringBuilder nsb2 = new NumberStringBuilder(); + FormattedStringBuilder nsb2 = new FormattedStringBuilder(); MicroProps micros2 = new MicroProps(true); mod.createImmutable().applyToMicros(micros2, fq); micros2.modMiddle.apply(nsb2, 0, 0); - NumberStringBuilder nsb3 = new NumberStringBuilder(); + FormattedStringBuilder nsb3 = new FormattedStringBuilder(); MicroProps micros3 = new MicroProps(false); mod.addToChain(micros3); mod.setPatternAttributes(SignDisplay.ALWAYS, false); @@ -115,7 +115,7 @@ public class MutablePatternModifierTest { mod.setNumberProperties(1, null); // Unsafe Code Path - NumberStringBuilder nsb = new NumberStringBuilder(); + FormattedStringBuilder nsb = new FormattedStringBuilder(); nsb.append("x123y", null); mod.apply(nsb, 1, 4); assertEquals("Unsafe Path", "xabcy", nsb.toString()); @@ -130,13 +130,13 @@ public class MutablePatternModifierTest { } private static String getPrefix(MutablePatternModifier mod) { - NumberStringBuilder nsb = new NumberStringBuilder(); + FormattedStringBuilder nsb = new FormattedStringBuilder(); mod.apply(nsb, 0, 0); return nsb.subSequence(0, mod.getPrefixLength()).toString(); } private static String getSuffix(MutablePatternModifier mod) { - NumberStringBuilder nsb = new NumberStringBuilder(); + FormattedStringBuilder nsb = new FormattedStringBuilder(); mod.apply(nsb, 0, 0); return nsb.subSequence(mod.getPrefixLength(), nsb.length()).toString(); } -- 2.40.0