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
#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 {
} // 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.
#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;
// UErrorCode is not available; fail silently.
uprv_free(newChars);
uprv_free(newFields);
- *this = NumberStringBuilder(); // can't fail
+ *this = FormattedStringBuilder(); // can't fail
return *this;
}
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;
}
return cp;
}
-UChar32 NumberStringBuilder::getLastCodePoint() const {
+UChar32 FormattedStringBuilder::getLastCodePoint() const {
if (fLength == 0) {
return -1;
}
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;
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)) {
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.
}
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);
}
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;
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;
return count;
}
-void NumberStringBuilder::writeTerminator(UErrorCode& status) {
+void FormattedStringBuilder::writeTerminator(UErrorCode& status) {
int32_t position = prepareForInsert(fLength, 1, status);
if (U_FAILURE(status)) {
return;
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);
}
}
-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();
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,
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"<NumberStringBuilder [", -1);
+ sb.append(u"<FormattedStringBuilder [", -1);
sb.append(toUnicodeString());
sb.append(u"] [", -1);
for (int i = 0; i < fLength; i++) {
return sb;
}
-const char16_t *NumberStringBuilder::chars() const {
+const char16_t *FormattedStringBuilder::chars() const {
return getCharPtr() + fZero;
}
-bool NumberStringBuilder::contentEquals(const NumberStringBuilder &other) const {
+bool FormattedStringBuilder::contentEquals(const FormattedStringBuilder &other) const {
if (fLength != other.fLength) {
return false;
}
return true;
}
-bool NumberStringBuilder::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 (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;
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 */
#include <cstdint>
-#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:
+ *
+ * <ol>
+ * <li>Efficient prepend as well as append.
+ * <li>Keeps tracks of Fields in an efficient manner.
+ * </ol>
+ *
+ * See also FormattedValueStringBuilderImpl.
+ *
+ * @author sffc (Shane Carr)
+ */
+class U_I18N_API FormattedStringBuilder : public UMemory {
private:
static const int32_t DEFAULT_CAPACITY = 40;
};
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;
UChar32 codePointBefore(int32_t index) const;
- NumberStringBuilder &clear();
+ FormattedStringBuilder &clear();
int32_t appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status);
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;
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;
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 <int32_t category, int32_t field>
+ 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<int8_t>((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
#include "fphdlimp.h"
#include "util.h"
#include "uvectr32.h"
-#include "number_stringbuilder.h"
+#include "formatted_string_builder.h"
/**
U_NAMESPACE_BEGIN
-/** Implementation using FieldPositionHandler to accept fields. */
+/**
+ * Implementation of FormattedValue using FieldPositionHandler to accept fields.
+ */
class FormattedValueFieldPositionIteratorImpl : public UMemory, public FormattedValue {
public:
};
-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):
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;
};
// 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);
}
<ClCompile Include="number_patternstring.cpp" />
<ClCompile Include="number_rounding.cpp" />
<ClCompile Include="number_scientific.cpp" />
- <ClCompile Include="number_stringbuilder.cpp" />
+ <ClCompile Include="formatted_string_builder.cpp" />
<ClCompile Include="number_utils.cpp" />
<ClCompile Include="number_mapper.cpp" />
<ClCompile Include="number_multiplier.cpp" />
<ClCompile Include="number_currencysymbols.cpp" />
<ClCompile Include="number_skeletons.cpp" />
<ClCompile Include="number_capi.cpp" />
- <ClCompile Include="numparse_stringsegment.cpp" />
+ <ClCompile Include="string_segment.cpp" />
<ClCompile Include="numparse_parsednumber.cpp" />
<ClCompile Include="numparse_impl.cpp" />
<ClCompile Include="numparse_symbols.cpp" />
<ClInclude Include="number_patternstring.h" />
<ClInclude Include="number_roundingutils.h" />
<ClInclude Include="number_scientific.h" />
- <ClInclude Include="number_stringbuilder.h" />
+ <ClInclude Include="formatted_string_builder.h" />
<ClInclude Include="number_types.h" />
<ClInclude Include="number_utypes.h" />
<ClInclude Include="number_utils.h" />
<ClInclude Include="number_multiplier.h" />
<ClInclude Include="number_currencysymbols.h" />
<ClInclude Include="number_skeletons.h" />
- <ClInclude Include="numparse_stringsegment.h" />
+ <ClInclude Include="string_segment.h" />
<ClInclude Include="numparse_impl.h" />
<ClInclude Include="numparse_symbols.h" />
<ClInclude Include="numparse_decimal.h" />
<ClCompile Include="number_scientific.cpp">
<Filter>formatting</Filter>
</ClCompile>
- <ClCompile Include="number_stringbuilder.cpp">
+ <ClCompile Include="formatted_string_builder.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_utils.cpp">
<ClCompile Include="number_capi.cpp">
<Filter>formatting</Filter>
</ClCompile>
- <ClCompile Include="numparse_stringsegment.cpp">
+ <ClCompile Include="string_segment.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numparse_parsednumber.cpp">
<ClInclude Include="number_scientific.h">
<Filter>formatting</Filter>
</ClInclude>
- <ClInclude Include="number_stringbuilder.h">
+ <ClInclude Include="formatted_string_builder.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="number_types.h">
<ClInclude Include="number_skeletons.h">
<Filter>formatting</Filter>
</ClInclude>
- <ClInclude Include="numparse_stringsegment.h">
+ <ClInclude Include="string_segment.h">
<Filter>formatting</Filter>
</ClInclude>
<ClInclude Include="numparse_impl.h">
<ClCompile Include="number_patternstring.cpp" />
<ClCompile Include="number_rounding.cpp" />
<ClCompile Include="number_scientific.cpp" />
- <ClCompile Include="number_stringbuilder.cpp" />
+ <ClCompile Include="formatted_string_builder.cpp" />
<ClCompile Include="number_utils.cpp" />
<ClCompile Include="number_mapper.cpp" />
<ClCompile Include="number_multiplier.cpp" />
<ClCompile Include="number_currencysymbols.cpp" />
<ClCompile Include="number_skeletons.cpp" />
<ClCompile Include="number_capi.cpp" />
- <ClCompile Include="numparse_stringsegment.cpp" />
+ <ClCompile Include="string_segment.cpp" />
<ClCompile Include="numparse_parsednumber.cpp" />
<ClCompile Include="numparse_impl.cpp" />
<ClCompile Include="numparse_symbols.cpp" />
<ClInclude Include="number_patternstring.h" />
<ClInclude Include="number_roundingutils.h" />
<ClInclude Include="number_scientific.h" />
- <ClInclude Include="number_stringbuilder.h" />
+ <ClInclude Include="formatted_string_builder.h" />
<ClInclude Include="number_types.h" />
<ClInclude Include="number_utypes.h" />
<ClInclude Include="number_utils.h" />
<ClInclude Include="number_multiplier.h" />
<ClInclude Include="number_currencysymbols.h" />
<ClInclude Include="number_skeletons.h" />
- <ClInclude Include="numparse_stringsegment.h" />
+ <ClInclude Include="string_segment.h" />
<ClInclude Include="numparse_impl.h" />
<ClInclude Include="numparse_symbols.h" />
<ClInclude Include="numparse_decimal.h" />
}
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;
#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 {
/**
* 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.
*
* <p>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);
// 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;
}
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;
}
void LocalizedNumberFormatter::getAffixImpl(bool isPrefix, bool isNegative, UnicodeString& result,
UErrorCode& status) const {
- NumberStringBuilder string;
+ FormattedStringBuilder string;
auto signum = static_cast<int8_t>(isNegative ? -1 : 1);
// Always return affixes for plural form OTHER.
static const StandardPlural::Form plural = StandardPlural::OTHER;
}
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; }
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);
}
// 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);
}
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.
}
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.
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);
}
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()) {
}
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;
}
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();
#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"
* 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);
/**
* 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.
/**
* 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 {
* 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:
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
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);
};
}
-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);
: 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);
}
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.
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;
}
-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) {
}
-CurrencySpacingEnabledModifier::CurrencySpacingEnabledModifier(const NumberStringBuilder &prefix,
- const NumberStringBuilder &suffix,
+CurrencySpacingEnabledModifier::CurrencySpacingEnabledModifier(const FormattedStringBuilder &prefix,
+ const FormattedStringBuilder &suffix,
bool overwrite,
bool strong,
const DecimalFormatSymbols &symbols,
}
}
-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;
}
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,
}
int32_t
-CurrencySpacingEnabledModifier::applyCurrencySpacingAffix(NumberStringBuilder &output, int32_t index,
+CurrencySpacingEnabledModifier::applyCurrencySpacingAffix(FormattedStringBuilder &output, int32_t index,
EAffix affix,
const DecimalFormatSymbols &symbols,
UErrorCode &status) {
#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 {
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;
// 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;
/**
* 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.
*
* <p>
* Formats a value that is already stored inside the StringBuilder <code>result</code> between the indices
* @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.
*
* <p>
- * Applies the compiled two-argument pattern to the NumberStringBuilder.
+ * Applies the compiled two-argument pattern to the FormattedStringBuilder.
*
* <p>
* 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);
/**
* 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)
fParameters(parameters) {}
ConstantMultiFieldModifier(
- const NumberStringBuilder &prefix,
- const NumberStringBuilder &suffix,
+ const FormattedStringBuilder &prefix,
+ const FormattedStringBuilder &suffix,
bool overwrite,
bool strong)
: fPrefix(prefix),
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;
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;
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);
};
/** 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
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;
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 {
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 {
#include "unicode/numberformatter.h"
#include "number_types.h"
-#include "number_stringbuilder.h"
+#include "formatted_string_builder.h"
#include "number_decimfmtprops.h"
using namespace icu;
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.
}
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();
}
ConstantMultiFieldModifier* MutablePatternModifier::createConstantModifier(UErrorCode& status) {
- NumberStringBuilder a;
- NumberStringBuilder b;
+ FormattedStringBuilder a;
+ FormattedStringBuilder b;
insertPrefix(a, 0, status);
insertSuffix(b, 0, status);
if (fPatternInfo->hasCurrencySign()) {
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.
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;
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;
* 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);
};
#include <cstdlib>
#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"
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;
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;
#include "unicode/numberformatter.h"
#include "uinvchar.h"
#include "charstr.h"
+#include "string_segment.h"
using namespace icu;
using namespace icu::number;
#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
#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;
class Modifier;
class MutablePatternModifier;
class DecimalQuantity;
-class NumberStringBuilder;
class ModifierStore;
struct MicroProps;
* 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;
/**
#include "number_roundingutils.h"
#include "decNumber.h"
#include "charstr.h"
+#include "formatted_string_builder.h"
U_NAMESPACE_BEGIN
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 <int32_t category, int32_t field>
- 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<int8_t>((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) {
#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 {
* 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;
#include "numparse_affixes.h"
#include "numparse_utils.h"
#include "number_utils.h"
+#include "string_segment.h"
using namespace icu;
using namespace icu::numparse;
#include "numparse_types.h"
#include "numparse_compositions.h"
+#include "string_segment.h"
#include "unicode/uniset.h"
using namespace icu;
#include "ucurrimp.h"
#include "unicode/errorcode.h"
#include "numparse_utils.h"
+#include "string_segment.h"
using namespace icu;
using namespace icu::numparse;
#include "unicode/uchar.h"
#include "putilimp.h"
#include "number_decimalquantity.h"
+#include "string_segment.h"
using namespace icu;
using namespace icu::numparse;
#include "unicode/localpointer.h"
#include "numparse_validators.h"
#include "number_multiplier.h"
+#include "string_segment.h"
U_NAMESPACE_BEGIN
#include "numparse_types.h"
#include "number_decimalquantity.h"
+#include "string_segment.h"
#include "putilimp.h"
#include <cmath>
#include "numparse_types.h"
#include "numparse_scientific.h"
#include "static_unicode_sets.h"
+#include "string_segment.h"
using namespace icu;
using namespace icu::numparse;
+++ /dev/null
-// © 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 */
#include "numparse_types.h"
#include "numparse_symbols.h"
#include "numparse_utils.h"
+#include "string_segment.h"
using namespace icu;
using namespace icu::numparse;
#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;
};
-/**
- * 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 <code>setOffset(getOffset()+delta)</code>.
- *
- * <p>
- * 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.
- *
- * <p>
- * <strong>Important:</strong> 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.
- *
- * <p>
- * 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.
- *
- * <p>
- * This method only returns offsets along code point boundaries.
- *
- * <p>
- * This method will perform case folding if case folding was enabled in the constructor.
- *
- * <p>
- * 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.
*
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 {
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 {
break;
}
- NumberStringBuilder& string = data.getStringRef();
+ FormattedStringBuilder& string = data.getStringRef();
int32_t lengthPrefix = 0;
int32_t length1 = 0;
int32_t lengthInfix = 0;
#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 {
* 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;
#include "uassert.h"
#include "number_decimalquantity.h"
#include "number_utypes.h"
-#include "number_stringbuilder.h"
+#include "formatted_string_builder.h"
U_NAMESPACE_BEGIN
double quantity,
const NumberFormat& fmt,
const PluralRules& rules,
- number::impl::NumberStringBuilder& output,
+ FormattedStringBuilder& output,
StandardPlural::Form& pluralForm,
UErrorCode& status) {
UnicodeString pluralKeyword;
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
/**
* 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.
double quantity,
const NumberFormat& fmt,
const PluralRules& rules,
- number::impl::NumberStringBuilder& output,
+ FormattedStringBuilder& output,
StandardPlural::Form& pluralForm,
UErrorCode& status);
#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"
static constexpr number::impl::Field kRDTNumericField
- = number::impl::NumFieldUtils::compress<UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_NUMERIC_FIELD>();
+ = StringBuilderFieldUtils::compress<UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_NUMERIC_FIELD>();
static constexpr number::impl::Field kRDTLiteralField
- = number::impl::NumFieldUtils::compress<UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_LITERAL_FIELD>();
+ = StringBuilderFieldUtils::compress<UFIELD_CATEGORY_RELATIVE_DATETIME, UDAT_REL_LITERAL_FIELD>();
-class FormattedRelativeDateTimeData : public FormattedValueNumberStringBuilderImpl {
+class FormattedRelativeDateTimeData : public FormattedValueStringBuilderImpl {
public:
- FormattedRelativeDateTimeData() : FormattedValueNumberStringBuilderImpl(kRDTNumericField) {}
+ FormattedRelativeDateTimeData() : FormattedValueStringBuilderImpl(kRDTNumericField) {}
virtual ~FormattedRelativeDateTimeData();
};
#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)
}
+U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */
--- /dev/null
+// © 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 <code>setOffset(getOffset()+delta)</code>.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * <strong>Important:</strong> 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.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * This method only returns offsets along code point boundaries.
+ *
+ * <p>
+ * This method will perform case folding if case folding was enabled in the constructor.
+ *
+ * <p>
+ * 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 */
// Forward declarations:
class IFixedDecimal;
class FieldPositionIteratorHandler;
+class FormattedStringBuilder;
namespace numparse {
namespace impl {
class RoundingImpl;
class ScientificHandler;
class Modifier;
-class NumberStringBuilder;
class AffixPatternProvider;
class NumberPropertyMapper;
struct DecimalFormatProperties;
}
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:
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
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)
#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"",
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);
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);
assertEqualsImpl(str, sb3);
UnicodeString sb4;
- NumberStringBuilder sb5;
+ FormattedStringBuilder sb5;
sb4.append(u"😇");
sb4.append(str);
sb4.append(u"xx");
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);
}
}
-void NumberStringBuilderTest::testSplice() {
+void FormattedStringBuilderTest::testSplice() {
static const struct TestCase {
const char16_t* input;
const int32_t startThis;
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);
}
}
-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);
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);
}
}
-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);
}
}
-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());
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++;
}
}
-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 #");
}
}
-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());
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());
}
}
+
+extern IntlTest *createFormattedStringBuilderTest() {
+ return new FormattedStringBuilderTest();
+}
+
#endif /* #if !UCONFIG_NO_FORMATTING */
</ClCompile>
<ClCompile Include="dtptngts.cpp" />
<ClCompile Include="fldset.cpp" />
+ <ClCompile Include="formatted_string_builder_test.cpp" />
<ClCompile Include="genderinfotest.cpp" />
<ClCompile Include="incaltst.cpp" />
<ClCompile Include="itformat.cpp" />
<ClCompile Include="numbertest_modifiers.cpp" />
<ClCompile Include="numbertest_patternmodifier.cpp" />
<ClCompile Include="numbertest_patternstring.cpp" />
- <ClCompile Include="numbertest_stringbuilder.cpp" />
- <ClCompile Include="numbertest_stringsegment.cpp" />
+ <ClCompile Include="string_segment_test.cpp" />
<ClCompile Include="numbertest_parse.cpp" />
<ClCompile Include="numbertest_doubleconversion.cpp" />
<ClCompile Include="numbertest_skeletons.cpp" />
<ClCompile Include="fldset.cpp">
<Filter>formatting</Filter>
</ClCompile>
+ <ClCompile Include="formatted_string_builder_test.cpp">
+ <Filter>formatting</Filter>
+ </ClCompile>
<ClCompile Include="genderinfotest.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numbertest_patternstring.cpp">
<Filter>formatting</Filter>
</ClCompile>
- <ClCompile Include="numbertest_stringbuilder.cpp">
- <Filter>formatting</Filter>
- </ClCompile>
- <ClCompile Include="numbertest_stringsegment.cpp">
+ <ClCompile Include="string_segment_test.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="numbertest_parse.cpp">
extern IntlTest *createNumberFormatSpecificationTest();
extern IntlTest *createScientificNumberFormatterTest();
extern IntlTest *createFormattedValueTest();
+extern IntlTest *createFormattedStringBuilderTest();
+extern IntlTest *createStringSegmentTest();
#define TESTCLASS(id, TestClass) \
callTest(*test, par);
}
break;
+ case 54:
+ name = "FormattedStringBuilderTest";
+ if (exec) {
+ logln("FormattedStringBuilderTest test---");
+ logln((UnicodeString)"");
+ LocalPointer<IntlTest> test(createFormattedStringBuilderTest());
+ callTest(*test, par);
+ }
+ break;
+ case 55:
+ name = "StringSegmentTest";
+ if (exec) {
+ logln("StringSegmentTest test---");
+ logln((UnicodeString)"");
+ LocalPointer<IntlTest> test(createStringSegmentTest());
+ callTest(*test, par);
+ }
+ break;
default: name = ""; break; //needed to end loop
}
if (exec) {
#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"
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);
};
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();
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
}
}
NumericSymbolProvider provider;
UErrorCode status = U_ZERO_ERROR;
- NumberStringBuilder sb;
+ FormattedStringBuilder sb;
for (auto& cas : cases) {
UnicodeString input(cas[0]);
UnicodeString expected(cas[1]);
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();
#include "putilimp.h"
#include "intltest.h"
-#include "number_stringbuilder.h"
+#include "formatted_string_builder.h"
#include "number_modifiers.h"
#include "numbertest.h"
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);
// 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];
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);
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);
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) {
}
UnicodeString debugString;
- debugString.append(u"<NumberStringBuilder [");
+ debugString.append(u"<FormattedStringBuilder [");
debugString.append(expectedChars);
debugString.append(u"] [");
debugString.append(expectedFields);
mod.setNumberProperties(1, StandardPlural::Form::COUNT);
// Unsafe Code Path
- NumberStringBuilder nsb;
+ FormattedStringBuilder nsb;
nsb.append(u"x123y", UNUM_FIELD_COUNT, status);
assertSuccess("Spot 3", status);
mod.apply(nsb, 1, 4, status);
DecimalQuantity fq;
fq.setToInt(1);
- NumberStringBuilder nsb1;
+ FormattedStringBuilder nsb1;
MicroProps micros1;
mod.addToChain(µs1);
mod.processQuantity(fq, micros1, status);
micros1.modMiddle->apply(nsb1, 0, 0, status);
assertSuccess("Spot 3", status);
- NumberStringBuilder nsb2;
+ FormattedStringBuilder nsb2;
MicroProps micros2;
LocalPointer<ImmutablePatternModifier> 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);
}
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);
#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 📻";
assertEquals("", 0, segment.getCommonPrefixLength(u"foo"));
}
+
+extern IntlTest *createStringSegmentTest() {
+ return new StringSegmentTest();
+}
+
#endif
// © 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:
*
* <ol>
* <li>Keeps tracks of Fields in an efficient manner.
* <li>String operations are fast-pathed to code point operations when possible.
* </ol>
+ *
+ * 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;
return Character.codePointBefore(chars, zero + index, zero);
}
- public NumberStringBuilder clear() {
+ public FormattedStringBuilder clear() {
zero = getCapacity() / 2;
length = 0;
return this;
}
/**
- * 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");
}
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;
*
* <p>
* 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("<NumberStringBuilder [");
+ sb.append("<FormattedStringBuilder [");
sb.append(this.toString());
sb.append("] [");
for (int i = zero; i < zero + length; i++) {
if (fields[i] == null) {
sb.append('n');
- } else {
+ } else if (fieldToDebugChar.containsKey(fields[i])) {
sb.append(fieldToDebugChar.get(fields[i]));
+ } else {
+ sb.append('?');
}
}
sb.append("]>");
* 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++) {
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);
- }
}
--- /dev/null
+// © 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);
+ }
+}
import com.ibm.icu.text.UnicodeSet;
/**
- * A mutable String wrapper with a variable offset and length and support for case folding.
- * <p>
- * The charAt, length, and subSequence methods all operate relative to the fixed offset into the String.
- * <p>
- * 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;
// 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;
/**
* 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.
*
* <p>
* Example input: "'-'¤x"; example output: "-$x"
* @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) {
import java.text.Format.Field;
+import com.ibm.icu.impl.FormattedStringBuilder;
+
/**
* The canonical implementation of {@link Modifier}, containing a prefix and suffix string.
*/
}
@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);
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;
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) {
}
@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);
@Override
public String toString() {
- NumberStringBuilder temp = new NumberStringBuilder();
+ FormattedStringBuilder temp = new FormattedStringBuilder();
apply(temp, 0, 0);
int prefixLength = getPrefixLength();
return String.format("<ConstantMultiFieldModifier prefix:'%s' suffix:'%s'>",
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;
/** Safe code path */
public CurrencySpacingEnabledModifier(
- NumberStringBuilder prefix,
- NumberStringBuilder suffix,
+ FormattedStringBuilder prefix,
+ FormattedStringBuilder suffix,
boolean overwrite,
boolean strong,
DecimalFormatSymbols symbols) {
/** 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
/** Unsafe code path */
public static int applyCurrencySpacing(
- NumberStringBuilder output,
+ FormattedStringBuilder output,
int prefixStart,
int prefixLen,
int suffixStart,
/** Unsafe code path */
private static int applyCurrencySpacingAffix(
- NumberStringBuilder output,
+ FormattedStringBuilder output,
int index,
byte affix,
DecimalFormatSymbols symbols) {
import java.text.Format.Field;
+import com.ibm.icu.impl.FormattedStringBuilder;
import com.ibm.icu.impl.StandardPlural;
/**
* 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
// 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;
* @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();
* 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()) {
}
@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.
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;
// 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
public int padAndApply(
Modifier mod1,
Modifier mod2,
- NumberStringBuilder string,
+ FormattedStringBuilder string,
int leftIndex,
int rightIndex) {
int modLength = mod1.getCodePointCount() + mod2.getCodePointCount();
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.
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;
}
@Override
- public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) {
+ public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) {
return formatAsPrefixSuffix(output, leftIndex, rightIndex);
}
* @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) {
/**
* 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.
*
* <p>
- * Applies the compiled two-argument pattern to the NumberStringBuilder.
+ * Applies the compiled two-argument pattern to the FormattedStringBuilder.
*
* <p>
* 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) {
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;
* @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;
}
*/
@Override
public boolean nextPosition(ConstrainedFieldPosition cfpos) {
- return string.nextPosition(cfpos, null);
+ return FormattedValueStringBuilderImpl.nextPosition(string, cfpos, null);
}
/**
*/
@Override
public AttributedCharacterIterator toCharacterIterator() {
- return string.toCharacterIterator(null);
+ return FormattedValueStringBuilderImpl.toCharacterIterator(string, null);
}
/**
*/
public boolean nextFieldPosition(FieldPosition fieldPosition) {
fq.populateUFieldPosition(fieldPosition);
- return string.nextFieldPosition(fieldPosition);
+ return FormattedValueStringBuilderImpl.nextFieldPosition(string, fieldPosition);
}
/**
*/
@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())
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())
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;
* @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;
*/
@Override
public boolean nextPosition(ConstrainedFieldPosition cfpos) {
- return string.nextPosition(cfpos, null);
+ return FormattedValueStringBuilderImpl.nextPosition(string, cfpos, null);
}
/**
* @see NumberRangeFormatter
*/
public boolean nextFieldPosition(FieldPosition fieldPosition) {
- return string.nextFieldPosition(fieldPosition);
+ return FormattedValueStringBuilderImpl.nextFieldPosition(string, fieldPosition);
}
/**
*/
@Override
public AttributedCharacterIterator toCharacterIterator() {
- return string.toCharacterIterator(null);
+ return FormattedValueStringBuilderImpl.toCharacterIterator(string, null);
}
/**
*/
@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();
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())
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;
return new LocalizedNumberFormatterAsFormat(this, resolve().loc);
}
- /** Helper method that creates a NumberStringBuilder and formats. */
+ /** Helper method that creates a FormattedStringBuilder and formats. */
private FormattedNumber format(DecimalQuantity fq) {
- NumberStringBuilder string = new NumberStringBuilder();
+ FormattedStringBuilder string = new FormattedStringBuilder();
formatImpl(fq, string);
return new FormattedNumber(string, fq);
}
* @deprecated ICU 60 This API is ICU internal only.
*/
@Deprecated
- public void formatImpl(DecimalQuantity fq, NumberStringBuilder string) {
+ public void formatImpl(DecimalQuantity fq, FormattedStringBuilder string) {
if (computeCompiled()) {
compiled.format(fq, string);
} else {
*/
@Deprecated
public String getAffixImpl(boolean isPrefix, boolean isNegative) {
- NumberStringBuilder string = new NumberStringBuilder();
+ FormattedStringBuilder string = new FormattedStringBuilder();
byte signum = (byte) (isNegative ? -1 : 1);
// Always return affixes for plural form OTHER.
StandardPlural plural = StandardPlural.OTHER;
package com.ibm.icu.number;
import com.ibm.icu.impl.CurrencyData;
+import com.ibm.icu.impl.FormattedStringBuilder;
import com.ibm.icu.impl.CurrencyData.CurrencyFormatInfo;
import com.ibm.icu.impl.StandardPlural;
import com.ibm.icu.impl.number.CompactData.CompactType;
import com.ibm.icu.impl.number.MicroPropsGenerator;
import com.ibm.icu.impl.number.MultiplierFormatHandler;
import com.ibm.icu.impl.number.MutablePatternModifier;
-import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.impl.number.Padder;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
public static int formatStatic(
MacroProps macros,
DecimalQuantity inValue,
- NumberStringBuilder outString) {
+ FormattedStringBuilder outString) {
MicroProps micros = preProcessUnsafe(macros, inValue);
int length = writeNumber(micros, inValue, outString, 0);
length += writeAffixes(micros, outString, 0, length);
MacroProps macros,
byte signum,
StandardPlural plural,
- NumberStringBuilder output) {
+ FormattedStringBuilder output) {
MicroProps micros = new MicroProps(false);
MicroPropsGenerator microPropsGenerator = macrosToMicroGenerator(macros, micros, false);
return getPrefixSuffixImpl(microPropsGenerator, signum, output);
/**
* Evaluates the "safe" MicroPropsGenerator created by "fromMacros".
*/
- public int format(DecimalQuantity inValue, NumberStringBuilder outString) {
+ public int format(DecimalQuantity inValue, FormattedStringBuilder outString) {
MicroProps micros = preProcess(inValue);
int length = writeNumber(micros, inValue, outString, 0);
length += writeAffixes(micros, outString, 0, length);
return micros;
}
- public int getPrefixSuffix(byte signum, StandardPlural plural, NumberStringBuilder output) {
+ public int getPrefixSuffix(byte signum, StandardPlural plural, FormattedStringBuilder output) {
return getPrefixSuffixImpl(microPropsGenerator, signum, output);
}
- private static int getPrefixSuffixImpl(MicroPropsGenerator generator, byte signum, NumberStringBuilder output) {
+ private static int getPrefixSuffixImpl(MicroPropsGenerator generator, byte signum, FormattedStringBuilder output) {
// #13453: DecimalFormat wants the affixes from the pattern only (modMiddle).
// TODO: Clean this up, closer to C++. The pattern modifier is not as accessible as in C++.
// Right now, ignore the plural form, run the pipeline with number 0, and get the modifier from the result.
*/
public static int writeAffixes(
MicroProps micros,
- NumberStringBuilder string,
+ FormattedStringBuilder string,
int start,
int end) {
// Always apply the inner modifier (which is "strong").
public static int writeNumber(
MicroProps micros,
DecimalQuantity quantity,
- NumberStringBuilder string,
+ FormattedStringBuilder string,
int index) {
int length = 0;
if (quantity.isInfinite()) {
private static int writeIntegerDigits(
MicroProps micros,
DecimalQuantity quantity,
- NumberStringBuilder string,
+ FormattedStringBuilder string,
int index) {
int length = 0;
int integerCount = quantity.getUpperDisplayMagnitude() + 1;
private static int writeFractionDigits(
MicroProps micros,
DecimalQuantity quantity,
- NumberStringBuilder string,
+ FormattedStringBuilder string,
int index) {
int length = 0;
int fractionCount = -quantity.getLowerDisplayMagnitude();
import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.ICUResourceBundle;
+import com.ibm.icu.impl.FormattedStringBuilder;
import com.ibm.icu.impl.PatternProps;
import com.ibm.icu.impl.SimpleFormatterImpl;
import com.ibm.icu.impl.StandardPlural;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.MicroProps;
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.impl.number.range.PrefixInfixSuffixLengthHelper;
import com.ibm.icu.impl.number.range.RangeMacroProps;
}
public FormattedNumberRange format(DecimalQuantity quantity1, DecimalQuantity quantity2, boolean equalBeforeRounding) {
- NumberStringBuilder string = new NumberStringBuilder();
+ FormattedStringBuilder string = new FormattedStringBuilder();
MicroProps micros1 = formatterImpl1.preProcess(quantity1);
MicroProps micros2;
if (fSameFormatters) {
return new FormattedNumberRange(string, quantity1, quantity2, identityResult);
}
- private void formatSingleValue(DecimalQuantity quantity1, DecimalQuantity quantity2, NumberStringBuilder string,
+ private void formatSingleValue(DecimalQuantity quantity1, DecimalQuantity quantity2, FormattedStringBuilder string,
MicroProps micros1, MicroProps micros2) {
if (fSameFormatters) {
int length = NumberFormatterImpl.writeNumber(micros1, quantity1, string, 0);
}
- private void formatApproximately(DecimalQuantity quantity1, DecimalQuantity quantity2, NumberStringBuilder string,
+ private void formatApproximately(DecimalQuantity quantity1, DecimalQuantity quantity2, FormattedStringBuilder string,
MicroProps micros1, MicroProps micros2) {
if (fSameFormatters) {
int length = NumberFormatterImpl.writeNumber(micros1, quantity1, string, 0);
}
}
- private void formatRange(DecimalQuantity quantity1, DecimalQuantity quantity2, NumberStringBuilder string,
+ private void formatRange(DecimalQuantity quantity1, DecimalQuantity quantity2, FormattedStringBuilder string,
MicroProps micros1, MicroProps micros2) {
// modInner is always notation (scientific); collapsable in ALL.
// modOuter is always units; collapsable in ALL, AUTO, and UNIT.
import java.text.Format.Field;
+import com.ibm.icu.impl.FormattedStringBuilder;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.MicroProps;
import com.ibm.icu.impl.number.MicroPropsGenerator;
import com.ibm.icu.impl.number.Modifier;
import com.ibm.icu.impl.number.MultiplierProducer;
-import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.impl.number.RoundingUtils;
import com.ibm.icu.number.NumberFormatter.SignDisplay;
import com.ibm.icu.number.Precision.SignificantRounderImpl;
}
@Override
- public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) {
+ public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) {
return doApply(exponent, output, rightIndex);
}
- private int doApply(int exponent, NumberStringBuilder output, int rightIndex) {
+ private int doApply(int exponent, FormattedStringBuilder output, int rightIndex) {
// FIXME: Localized exponent separator location.
int i = rightIndex;
// Append the exponent separator and sign
}
@Override
- public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) {
+ public int apply(FormattedStringBuilder output, int leftIndex, int rightIndex) {
return handler.doApply(exponent, output, rightIndex);
}
import java.util.Locale;
import com.ibm.icu.impl.CacheBase;
+import com.ibm.icu.impl.FormattedValueStringBuilderImpl;
import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.ICUResourceBundle;
+import com.ibm.icu.impl.FormattedStringBuilder;
import com.ibm.icu.impl.SimpleFormatterImpl;
import com.ibm.icu.impl.SoftCache;
import com.ibm.icu.impl.StandardPlural;
import com.ibm.icu.impl.UResource;
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.SimpleModifier;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.util.Calendar;
*/
public static class FormattedRelativeDateTime implements FormattedValue {
- private final NumberStringBuilder string;
+ private final FormattedStringBuilder string;
- private FormattedRelativeDateTime(NumberStringBuilder string) {
+ private FormattedRelativeDateTime(FormattedStringBuilder string) {
this.string = string;
}
*/
@Override
public boolean nextPosition(ConstrainedFieldPosition cfpos) {
- return string.nextPosition(cfpos, Field.NUMERIC);
+ return FormattedValueStringBuilderImpl.nextPosition(string, cfpos, Field.NUMERIC);
}
/**
*/
@Override
public AttributedCharacterIterator toCharacterIterator() {
- return string.toCharacterIterator(Field.NUMERIC);
+ return FormattedValueStringBuilderImpl.toCharacterIterator(string, Field.NUMERIC);
}
}
* @stable ICU 53
*/
public String format(double quantity, Direction direction, RelativeUnit unit) {
- NumberStringBuilder output = formatImpl(quantity, direction, unit);
+ FormattedStringBuilder output = formatImpl(quantity, direction, unit);
return adjustForContext(output.toString());
}
}
/** Implementation method for format and formatToValue with RelativeUnit */
- private NumberStringBuilder formatImpl(double quantity, Direction direction, RelativeUnit unit) {
+ private FormattedStringBuilder formatImpl(double quantity, Direction direction, RelativeUnit unit) {
if (direction != Direction.LAST && direction != Direction.NEXT) {
throw new IllegalArgumentException("direction must be NEXT or LAST");
}
int pastFutureIndex = (direction == Direction.NEXT ? 1 : 0);
- NumberStringBuilder output = new NumberStringBuilder();
+ FormattedStringBuilder output = new FormattedStringBuilder();
String pluralKeyword;
if (numberFormat instanceof DecimalFormat) {
DecimalQuantity dq = new DecimalQuantity_DualStorageBCD(quantity);
* @stable ICU 57
*/
public String formatNumeric(double offset, RelativeDateTimeUnit unit) {
- NumberStringBuilder output = formatNumericImpl(offset, unit);
+ FormattedStringBuilder output = formatNumericImpl(offset, unit);
return adjustForContext(output.toString());
}
}
/** Implementation method for formatNumeric and formatNumericToValue */
- private NumberStringBuilder formatNumericImpl(double offset, RelativeDateTimeUnit unit) {
+ private FormattedStringBuilder formatNumericImpl(double offset, RelativeDateTimeUnit unit) {
// TODO:
// The full implementation of this depends on CLDR data that is not yet available,
// see: http://unicode.org/cldr/trac/ticket/9165 Add more relative field data.
if (string == null) {
return null;
}
- NumberStringBuilder nsb = new NumberStringBuilder();
+ FormattedStringBuilder nsb = new FormattedStringBuilder();
nsb.append(string, Field.LITERAL);
return new FormattedRelativeDateTime(nsb);
}
public FormattedRelativeDateTime formatToValue(double offset, RelativeDateTimeUnit unit) {
checkNoAdjustForContext();
CharSequence cs = formatRelativeImpl(offset, unit);
- NumberStringBuilder nsb;
- if (cs instanceof NumberStringBuilder) {
- nsb = (NumberStringBuilder) cs;
+ FormattedStringBuilder nsb;
+ if (cs instanceof FormattedStringBuilder) {
+ nsb = (FormattedStringBuilder) cs;
} else {
- nsb = new NumberStringBuilder();
+ nsb = new FormattedStringBuilder();
nsb.append(cs, Field.LITERAL);
}
return new FormattedRelativeDateTime(nsb);
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package com.ibm.icu.dev.test.number;
+package com.ibm.icu.dev.test.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.Test;
-import com.ibm.icu.impl.number.NumberStringBuilder;
+import com.ibm.icu.impl.FormattedValueStringBuilderImpl;
+import com.ibm.icu.impl.FormattedStringBuilder;
import com.ibm.icu.text.NumberFormat;
/** @author sffc */
-public class NumberStringBuilderTest {
+public class FormattedStringBuilderTest {
private static final String[] EXAMPLE_STRINGS = {
"",
"xyz",
public void testInsertAppendCharSequence() {
StringBuilder sb1 = new StringBuilder();
- NumberStringBuilder sb2 = new NumberStringBuilder();
+ FormattedStringBuilder sb2 = new FormattedStringBuilder();
for (String str : EXAMPLE_STRINGS) {
- NumberStringBuilder sb3 = new NumberStringBuilder();
+ FormattedStringBuilder sb3 = new FormattedStringBuilder();
sb1.append(str);
sb2.append(str, null);
sb3.append(str, null);
assertCharSequenceEquals(sb3, str);
StringBuilder sb4 = new StringBuilder();
- NumberStringBuilder sb5 = new NumberStringBuilder();
+ FormattedStringBuilder sb5 = new FormattedStringBuilder();
sb4.append("😇");
sb4.append(str);
sb4.append("xx");
assertCharSequenceEquals(sb4, sb5);
sb4.append(sb4.toString());
- sb5.append(new NumberStringBuilder(sb5));
+ sb5.append(new FormattedStringBuilder(sb5));
assertCharSequenceEquals(sb4, sb5);
}
}
{ "lorem ipsum dolor sit amet", 8, 18 } }; // 10 chars, larger than several replacements
StringBuilder sb1 = new StringBuilder();
- NumberStringBuilder sb2 = new NumberStringBuilder();
+ FormattedStringBuilder sb2 = new FormattedStringBuilder();
for (Object[] cas : cases) {
String input = (String) cas[0];
int startThis = (Integer) cas[1];
int[] cases = { 0, 1, 60, 127, 128, 0x7fff, 0x8000, 0xffff, 0x10000, 0x1f000, 0x10ffff };
StringBuilder sb1 = new StringBuilder();
- NumberStringBuilder sb2 = new NumberStringBuilder();
+ FormattedStringBuilder sb2 = new FormattedStringBuilder();
for (int cas : cases) {
- NumberStringBuilder sb3 = new NumberStringBuilder();
+ FormattedStringBuilder sb3 = new FormattedStringBuilder();
sb1.appendCodePoint(cas);
sb2.appendCodePoint(cas, null);
sb3.appendCodePoint(cas, null);
assertEquals(Character.codePointAt(sb3, 0), cas);
StringBuilder sb4 = new StringBuilder();
- NumberStringBuilder sb5 = new NumberStringBuilder();
+ FormattedStringBuilder sb5 = new FormattedStringBuilder();
sb4.append("😇");
sb4.appendCodePoint(cas); // Java StringBuilder has no insertCodePoint()
sb4.append("xx");
@Test
public void testCopy() {
for (String str : EXAMPLE_STRINGS) {
- NumberStringBuilder sb1 = new NumberStringBuilder();
+ FormattedStringBuilder sb1 = new FormattedStringBuilder();
sb1.append(str, null);
- NumberStringBuilder sb2 = new NumberStringBuilder(sb1);
+ FormattedStringBuilder sb2 = new FormattedStringBuilder(sb1);
assertCharSequenceEquals(sb1, sb2);
assertTrue(sb1.contentEquals(sb2));
@Test
public void testFields() {
for (String str : EXAMPLE_STRINGS) {
- NumberStringBuilder sb = new NumberStringBuilder();
+ FormattedStringBuilder sb = new FormattedStringBuilder();
sb.append(str, null);
sb.append(str, NumberFormat.Field.CURRENCY);
Field[] fields = sb.toFieldArray();
// Very basic FieldPosition test. More robust tests happen in NumberFormatTest.
// Let NumberFormatTest also take care of AttributedCharacterIterator material.
FieldPosition fp = new FieldPosition(NumberFormat.Field.CURRENCY);
- sb.nextFieldPosition(fp);
+ FormattedValueStringBuilderImpl.nextFieldPosition(sb, fp);
assertEquals(str.length(), fp.getBeginIndex());
assertEquals(str.length() * 2, fp.getEndIndex());
assertEquals(fields[2], NumberFormat.Field.INTEGER);
}
- sb.append(new NumberStringBuilder(sb));
+ sb.append(new FormattedStringBuilder(sb));
sb.append(sb.toCharArray(), sb.toFieldArray());
int numNull = 0;
int numCurr = 0;
assertEquals(numNull, numCurr);
assertEquals(str.length() > 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()));
@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);
@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());
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));
}
}
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 {
}
};
- NumberStringBuilder sb = new NumberStringBuilder();
+ FormattedStringBuilder sb = new FormattedStringBuilder();
for (String[] cas : cases) {
String input = cas[0];
String expected = cas[1];
}
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();
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;
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();
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;
@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");
// 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];
@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");
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));
boolean expectedStrong,
String expectedChars,
String expectedFields) {
- NumberStringBuilder sb = new NumberStringBuilder();
+ FormattedStringBuilder sb = new FormattedStringBuilder();
sb.appendCodePoint('|', null);
assertModifierEquals(mod,
sb,
private void assertModifierEquals(
Modifier mod,
- NumberStringBuilder sb,
+ FormattedStringBuilder sb,
int expectedPrefixLength,
boolean expectedStrong,
String expectedChars,
sb.codePointCount() - oldCount,
mod.getCodePointCount());
}
- assertEquals("<NumberStringBuilder [" + expectedChars + "] [" + expectedFields + "]>",
+ assertEquals("<FormattedStringBuilder [" + expectedChars + "] [" + expectedFields + "]>",
sb.toDebugString());
}
}
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;
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);
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());
}
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();
}