ICU-20666 Adding insert/appendChar16 to FormattedStringBuilder.
diff --git a/icu4c/source/i18n/formatted_string_builder.cpp b/icu4c/source/i18n/formatted_string_builder.cpp
index 4ffc4c7..3024bff 100644
--- a/icu4c/source/i18n/formatted_string_builder.cpp
+++ b/icu4c/source/i18n/formatted_string_builder.cpp
@@ -144,10 +144,6 @@
return *this;
}
-int32_t FormattedStringBuilder::appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status) {
- return insertCodePoint(fLength, codePoint, field, status);
-}
-
int32_t
FormattedStringBuilder::insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status) {
int32_t count = U16_LENGTH(codePoint);
@@ -166,10 +162,6 @@
return count;
}
-int32_t FormattedStringBuilder::append(const UnicodeString &unistr, Field field, UErrorCode &status) {
- return insert(fLength, unistr, field, status);
-}
-
int32_t FormattedStringBuilder::insert(int32_t index, const UnicodeString &unistr, Field field,
UErrorCode &status) {
if (unistr.length() == 0) {
diff --git a/icu4c/source/i18n/formatted_string_builder.h b/icu4c/source/i18n/formatted_string_builder.h
index cc13b66..2949ae7 100644
--- a/icu4c/source/i18n/formatted_string_builder.h
+++ b/icu4c/source/i18n/formatted_string_builder.h
@@ -85,29 +85,73 @@
FormattedStringBuilder &clear();
- int32_t appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status);
+ /** Appends a UTF-16 code unit. */
+ inline int32_t appendChar16(char16_t codeUnit, Field field, UErrorCode& status) {
+ // appendCodePoint handles both code units and code points.
+ return insertCodePoint(fLength, codeUnit, field, status);
+ }
+ /** Inserts a UTF-16 code unit. Note: insert at index 0 is very efficient. */
+ inline int32_t insertChar16(int32_t index, char16_t codeUnit, Field field, UErrorCode& status) {
+ // insertCodePoint handles both code units and code points.
+ return insertCodePoint(index, codeUnit, field, status);
+ }
+
+ /** Appends a Unicode code point. */
+ inline int32_t appendCodePoint(UChar32 codePoint, Field field, UErrorCode &status) {
+ return insertCodePoint(fLength, codePoint, field, status);
+ }
+
+ /** Inserts a Unicode code point. Note: insert at index 0 is very efficient. */
int32_t insertCodePoint(int32_t index, UChar32 codePoint, Field field, UErrorCode &status);
- int32_t append(const UnicodeString &unistr, Field field, UErrorCode &status);
+ /** Appends a string. */
+ inline int32_t append(const UnicodeString &unistr, Field field, UErrorCode &status) {
+ return insert(fLength, unistr, field, status);
+ }
+ /** Inserts a string. Note: insert at index 0 is very efficient. */
int32_t insert(int32_t index, const UnicodeString &unistr, Field field, UErrorCode &status);
+ /** Inserts a substring. Note: insert at index 0 is very efficient.
+ *
+ * @param start Start index of the substring of unistr to be inserted.
+ * @param end End index of the substring of unistr to be inserted (exclusive).
+ */
int32_t insert(int32_t index, const UnicodeString &unistr, int32_t start, int32_t end, Field field,
UErrorCode &status);
+ /** Deletes a substring and then inserts a string at that same position.
+ * Similar to JavaScript Array.prototype.splice().
+ *
+ * @param startThis Start of the span to delete.
+ * @param endThis End of the span to delete (exclusive).
+ * @param unistr The string to insert at the deletion position.
+ * @param startOther Start index of the substring of unistr to be inserted.
+ * @param endOther End index of the substring of unistr to be inserted (exclusive).
+ */
int32_t splice(int32_t startThis, int32_t endThis, const UnicodeString &unistr,
int32_t startOther, int32_t endOther, Field field, UErrorCode& status);
+ /** Appends a formatted string. */
int32_t append(const FormattedStringBuilder &other, UErrorCode &status);
+ /** Inserts a formatted string. Note: insert at index 0 is very efficient. */
int32_t insert(int32_t index, const FormattedStringBuilder &other, UErrorCode &status);
+ /**
+ * Ensures that the string buffer contains a NUL terminator. The NUL terminator does
+ * not count toward the string length. Any further changes to the string (insert or
+ * append) may invalidate the NUL terminator.
+ *
+ * You should call this method after the formatted string is completely built if you
+ * plan to return a pointer to the string from a C API.
+ */
void writeTerminator(UErrorCode& status);
/**
* Gets a "safe" UnicodeString that can be used even after the FormattedStringBuilder is destructed.
- * */
+ */
UnicodeString toUnicodeString() const;
/**
diff --git a/icu4c/source/i18n/measfmt.cpp b/icu4c/source/i18n/measfmt.cpp
index 3c4d5d6..8f306c0 100644
--- a/icu4c/source/i18n/measfmt.cpp
+++ b/icu4c/source/i18n/measfmt.cpp
@@ -777,7 +777,7 @@
case u'm':
case u's':
if (protect) {
- fsb.appendCodePoint(c, undefinedField, status);
+ fsb.appendChar16(c, undefinedField, status);
} else {
UnicodeString tmp;
if ((i + 1 < patternLength) && pattern[i + 1] == c) { // doubled
@@ -793,14 +793,14 @@
case u'\'':
// '' is escaped apostrophe
if ((i + 1 < patternLength) && pattern[i + 1] == c) {
- fsb.appendCodePoint(c, undefinedField, status);
+ fsb.appendChar16(c, undefinedField, status);
i++;
} else {
protect = !protect;
}
break;
default:
- fsb.appendCodePoint(c, undefinedField, status);
+ fsb.appendChar16(c, undefinedField, status);
}
}
diff --git a/icu4c/source/test/intltest/formatted_string_builder_test.cpp b/icu4c/source/test/intltest/formatted_string_builder_test.cpp
index b0c4552..4ce63da 100644
--- a/icu4c/source/test/intltest/formatted_string_builder_test.cpp
+++ b/icu4c/source/test/intltest/formatted_string_builder_test.cpp
@@ -175,6 +175,18 @@
sb5.insertCodePoint(2, cas, UNUM_FIELD_COUNT, status);
assertSuccess("Inserting into sb5", status);
assertEqualsImpl(sb4, sb5);
+
+ UnicodeString sb6;
+ FormattedStringBuilder sb7;
+ sb6.append(cas);
+ if (U_IS_SUPPLEMENTARY(cas)) {
+ sb7.appendChar16(U16_TRAIL(cas), UNUM_FIELD_COUNT, status);
+ sb7.insertChar16(0, U16_LEAD(cas), UNUM_FIELD_COUNT, status);
+ } else {
+ sb7.insertChar16(0, cas, UNUM_FIELD_COUNT, status);
+ }
+ assertSuccess("Insert/append into sb7", status);
+ assertEqualsImpl(sb6, sb7);
}
}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java
index ca3cb18..0eb54f5 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/FormattedStringBuilder.java
@@ -106,6 +106,18 @@
return this;
}
+ public int appendChar16(char codeUnit, Field field) {
+ return insertChar16(length, codeUnit, field);
+ }
+
+ public int insertChar16(int index, char codeUnit, Field field) {
+ int count = 1;
+ int position = prepareForInsert(index, count);
+ chars[position] = codeUnit;
+ fields[position] = field;
+ return count;
+ }
+
/**
* Appends the specified codePoint to the end of the string.
*
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
index d3f2306..4925c63 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
@@ -904,7 +904,7 @@
case 'm':
case 's':
if (protect) {
- fsb.appendCodePoint(c, null);
+ fsb.appendChar16(c, null);
} else {
if ((i + 1 < pattern.length()) && pattern.charAt(i + 1) == c) { // doubled
fsb.append(numberFormatter2.format(value), null); // TODO: Use proper Field
@@ -917,14 +917,14 @@
case '\'':
// '' is escaped apostrophe
if ((i + 1 < pattern.length()) && pattern.charAt(i + 1) == c) {
- fsb.appendCodePoint(c, null);
+ fsb.appendChar16(c, null);
i++;
} else {
protect = !protect;
}
break;
default:
- fsb.appendCodePoint(c, null);
+ fsb.appendChar16(c, null);
}
}
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedStringBuilderTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedStringBuilderTest.java
index caf2162..b297b1f 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedStringBuilderTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedStringBuilderTest.java
@@ -37,8 +37,8 @@
sb1.append(str);
sb2.append(str, null);
sb3.append(str, null);
- assertCharSequenceEquals(sb1, sb2);
- assertCharSequenceEquals(sb3, str);
+ assertCharSequenceEquals(str, sb1, sb2);
+ assertCharSequenceEquals(str, sb3, str);
StringBuilder sb4 = new StringBuilder();
FormattedStringBuilder sb5 = new FormattedStringBuilder();
@@ -47,25 +47,25 @@
sb4.append("xx");
sb5.append("😇xx", null);
sb5.insert(2, str, null);
- assertCharSequenceEquals(sb4, sb5);
+ assertCharSequenceEquals(str, sb4, sb5);
int start = Math.min(1, str.length());
int end = Math.min(10, str.length());
sb4.insert(3, str, start, end);
sb5.insert(3, str, start, end, null);
- assertCharSequenceEquals(sb4, sb5);
+ assertCharSequenceEquals(str, sb4, sb5);
sb4.append(str.toCharArray());
sb5.append(str.toCharArray(), null);
- assertCharSequenceEquals(sb4, sb5);
+ assertCharSequenceEquals(str, sb4, sb5);
sb4.insert(4, str.toCharArray());
sb5.insert(4, str.toCharArray(), null);
- assertCharSequenceEquals(sb4, sb5);
+ assertCharSequenceEquals(str, sb4, sb5);
sb4.append(sb4.toString());
sb5.append(new FormattedStringBuilder(sb5));
- assertCharSequenceEquals(sb4, sb5);
+ assertCharSequenceEquals(str, sb4, sb5);
}
}
@@ -96,7 +96,7 @@
sb2.clear();
sb2.append(input, null);
sb2.splice(startThis, endThis, replacement, 0, replacement.length(), null);
- assertCharSequenceEquals(sb1, sb2);
+ assertCharSequenceEquals(input, sb1, sb2);
// Test replacement with partial string
if (replacement.length() <= 2) {
@@ -108,7 +108,7 @@
sb2.clear();
sb2.append(input, null);
sb2.splice(startThis, endThis, replacement, 1, 3, null);
- assertCharSequenceEquals(sb1, sb2);
+ assertCharSequenceEquals(input, sb1, sb2);
}
}
}
@@ -124,7 +124,7 @@
sb1.appendCodePoint(cas);
sb2.appendCodePoint(cas, null);
sb3.appendCodePoint(cas, null);
- assertCharSequenceEquals(sb1, sb2);
+ assertCharSequenceEquals(Integer.toString(cas), sb1, sb2);
assertEquals(Character.codePointAt(sb3, 0), cas);
StringBuilder sb4 = new StringBuilder();
@@ -134,7 +134,18 @@
sb4.append("xx");
sb5.append("😇xx", null);
sb5.insertCodePoint(2, cas, null);
- assertCharSequenceEquals(sb4, sb5);
+ assertCharSequenceEquals(Integer.toString(cas), sb4, sb5);
+
+ StringBuilder sb6 = new StringBuilder();
+ FormattedStringBuilder sb7 = new FormattedStringBuilder();
+ sb6.appendCodePoint(cas);
+ if (Character.charCount(cas) == 2) {
+ sb7.appendChar16(Character.lowSurrogate(cas), null);
+ sb7.insertChar16(0, Character.highSurrogate(cas), null);
+ } else {
+ sb7.insertChar16(0, (char) cas, null);
+ }
+ assertCharSequenceEquals(Integer.toString(cas), sb6, sb7);
}
}
@@ -144,7 +155,7 @@
FormattedStringBuilder sb1 = new FormattedStringBuilder();
sb1.append(str, null);
FormattedStringBuilder sb2 = new FormattedStringBuilder(sb1);
- assertCharSequenceEquals(sb1, sb2);
+ assertCharSequenceEquals(str, sb1, sb2);
assertTrue(sb1.contentEquals(sb2));
sb1.append("12345", null);
@@ -251,21 +262,21 @@
assertEquals("Code point count is 2", 2, nsb.codePointCount());
}
- private static void assertCharSequenceEquals(CharSequence a, CharSequence b) {
- assertEquals(a.toString(), b.toString());
+ private static void assertCharSequenceEquals(String msg, CharSequence a, CharSequence b) {
+ assertEquals(msg, a.toString(), b.toString());
- assertEquals(a.length(), b.length());
+ assertEquals(msg, a.length(), b.length());
for (int i = 0; i < a.length(); i++) {
- assertEquals(a.charAt(i), b.charAt(i));
+ assertEquals(msg, a.charAt(i), b.charAt(i));
}
int start = Math.min(2, a.length());
int end = Math.min(12, a.length());
if (start != end) {
- assertCharSequenceEquals(a.subSequence(start, end), b.subSequence(start, end));
+ assertCharSequenceEquals(msg, a.subSequence(start, end), b.subSequence(start, end));
if (b instanceof FormattedStringBuilder) {
FormattedStringBuilder bnsb = (FormattedStringBuilder) b;
- assertCharSequenceEquals(a.subSequence(start, end), bnsb.subString(start, end));
+ assertCharSequenceEquals(msg, a.subSequence(start, end), bnsb.subString(start, end));
}
}
}