ICU-20666 Adding insert/appendChar16 to FormattedStringBuilder.

This commit is contained in:
Shane F. Carr 2019-06-22 09:48:29 -07:00
parent 5c23416308
commit 48df66704c
7 changed files with 106 additions and 35 deletions

View File

@ -144,10 +144,6 @@ FormattedStringBuilder &FormattedStringBuilder::clear() {
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 @@ FormattedStringBuilder::insertCodePoint(int32_t index, UChar32 codePoint, Field
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) {

View File

@ -85,29 +85,73 @@ class U_I18N_API FormattedStringBuilder : public UMemory {
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;
/**

View File

@ -777,7 +777,7 @@ UnicodeString &MeasureFormat::formatNumeric(
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 @@ UnicodeString &MeasureFormat::formatNumeric(
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);
}
}

View File

@ -175,6 +175,18 @@ void FormattedStringBuilderTest::testInsertAppendCodePoint() {
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);
}
}

View File

@ -106,6 +106,18 @@ public class FormattedStringBuilder implements CharSequence {
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.
*

View File

@ -904,7 +904,7 @@ public class MeasureFormat extends UFormat {
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 @@ public class MeasureFormat extends UFormat {
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);
}
}

View File

@ -37,8 +37,8 @@ public class FormattedStringBuilderTest {
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 @@ public class FormattedStringBuilderTest {
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 @@ public class FormattedStringBuilderTest {
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 @@ public class FormattedStringBuilderTest {
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 @@ public class FormattedStringBuilderTest {
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 @@ public class FormattedStringBuilderTest {
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 @@ public class FormattedStringBuilderTest {
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 @@ public class FormattedStringBuilderTest {
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));
}
}
}