You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2025/08/30 17:35:01 UTC

(commons-text) 02/02: [TEXT-236] Inserting at end of a TextStringBuilder throws a StringIndexOutOfBoundsException

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-text.git

commit 53cf6ece273622821c5aa13444800ba94a69be55
Author: Gary D. Gregory <ga...@gmail.com>
AuthorDate: Sat Aug 30 13:34:54 2025 -0400

    [TEXT-236] Inserting at end of a TextStringBuilder throws a
    StringIndexOutOfBoundsException
---
 src/changes/changes.xml                            |  1 +
 src/conf/spotbugs-exclude-filter.xml               |  6 +++
 .../org/apache/commons/text/TextStringBuilder.java | 14 +++----
 .../text/TextStringBuilderAppendInsertTest.java    | 46 ++++++++++++++++++++++
 4 files changed, 60 insertions(+), 7 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index df97b651..362314fa 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -48,6 +48,7 @@ The <action> type attribute can be add,update,fix,remove.
   <release version="1.14.1" date="YYYY-MM-DD" description="This is a feature and maintenance release. Java 8 or later is required.">
       <!-- FIX -->
       <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix exception message typo in XmlStringLookup.XmlStringLookup(Map, Path...).</action>
+      <action type="fix" dev="ggregory" due-to="Sumit Bera, Alex Herbert, Gary Gregory" issue="TEXT-236">Inserting at end of a TextStringBuilder throws a StringIndexOutOfBoundsException.</action>
       <!-- ADD -->
       <action type="add" dev="ggregory" due-to="Piotr P. Karwasz, Gary Gregory">Add experimental CycloneDX VEX file #683.</action>
       <!-- UPDATE -->
diff --git a/src/conf/spotbugs-exclude-filter.xml b/src/conf/spotbugs-exclude-filter.xml
index 546e6193..c2b01f6b 100644
--- a/src/conf/spotbugs-exclude-filter.xml
+++ b/src/conf/spotbugs-exclude-filter.xml
@@ -73,4 +73,10 @@
     <Class name="org.apache.commons.text.similarity.LevenshteinDistance" />
     <Bug pattern="SING_SINGLETON_HAS_NONPRIVATE_CONSTRUCTOR" />
   </Match>
+  <!-- RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT false positive: Exceptions can be thrown -->
+  <Match>
+    <Class name="org.apache.commons.text.TextStringBuilder" />
+    <Method name="insert" />
+    <Bug pattern="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT" />
+  </Match>
 </FindBugsFilter>
diff --git a/src/main/java/org/apache/commons/text/TextStringBuilder.java b/src/main/java/org/apache/commons/text/TextStringBuilder.java
index 1f194220..503bb056 100644
--- a/src/main/java/org/apache/commons/text/TextStringBuilder.java
+++ b/src/main/java/org/apache/commons/text/TextStringBuilder.java
@@ -2190,7 +2190,7 @@ public class TextStringBuilder implements CharSequence, Appendable, Serializable
      * @throws IndexOutOfBoundsException if the index is invalid
      */
     public TextStringBuilder insert(final int index, final boolean value) {
-        validateIndex(index);
+        validateRange(index, size);
         if (value) {
             ensureCapacityInternal(size + TRUE_STRING_SIZE);
             System.arraycopy(buffer, index, buffer, index + TRUE_STRING_SIZE, size - index);
@@ -2212,7 +2212,7 @@ public class TextStringBuilder implements CharSequence, Appendable, Serializable
      * @throws IndexOutOfBoundsException if the index is invalid
      */
     public TextStringBuilder insert(final int index, final char value) {
-        validateIndex(index);
+        validateRange(index, size);
         ensureCapacityInternal(size + 1);
         System.arraycopy(buffer, index, buffer, index + 1, size - index);
         buffer[index] = value;
@@ -2229,7 +2229,7 @@ public class TextStringBuilder implements CharSequence, Appendable, Serializable
      * @throws IndexOutOfBoundsException if the index is invalid
      */
     public TextStringBuilder insert(final int index, final char[] chars) {
-        validateIndex(index);
+        validateRange(index, size);
         if (chars == null) {
             return insert(index, nullText);
         }
@@ -2254,7 +2254,7 @@ public class TextStringBuilder implements CharSequence, Appendable, Serializable
      * @throws IndexOutOfBoundsException if any index is invalid
      */
     public TextStringBuilder insert(final int index, final char[] chars, final int offset, final int length) {
-        validateIndex(index);
+        validateRange(index, size);
         if (chars == null) {
             return insert(index, nullText);
         }
@@ -2346,7 +2346,7 @@ public class TextStringBuilder implements CharSequence, Appendable, Serializable
      * @throws IndexOutOfBoundsException if the index is invalid
      */
     public TextStringBuilder insert(final int index, String str) {
-        validateIndex(index);
+        validateRange(index, size);
         if (str == null) {
             str = nullText;
         }
@@ -3208,10 +3208,10 @@ public class TextStringBuilder implements CharSequence, Appendable, Serializable
     }
 
     /**
-     * Validates that an index is in the range {@code 0 <= index <= size}.
+     * Validates that an index is in the range {@code 0 <= index < size}.
      *
      * @param index the index to test.
-     * @throws IndexOutOfBoundsException Thrown when the index is not the range {@code 0 <= index <= size}.
+     * @throws IndexOutOfBoundsException Thrown when the index is not the range {@code 0 <= index < size}.
      */
     protected void validateIndex(final int index) {
         if (index < 0 || index >= size) {
diff --git a/src/test/java/org/apache/commons/text/TextStringBuilderAppendInsertTest.java b/src/test/java/org/apache/commons/text/TextStringBuilderAppendInsertTest.java
index 6d7a33c1..51e431f5 100644
--- a/src/test/java/org/apache/commons/text/TextStringBuilderAppendInsertTest.java
+++ b/src/test/java/org/apache/commons/text/TextStringBuilderAppendInsertTest.java
@@ -1133,6 +1133,52 @@ class TextStringBuilderAppendInsertTest {
         assertEquals("4.5barbaz", sb.toString());
     }
 
+    @Test
+    void testInsertAtEnd() {
+        final TextStringBuilder sb = new TextStringBuilder();
+        assertEquals("", sb.toString());
+        sb.insert(0, "Hello");
+        assertEquals("Hello", sb.toString());
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(-1, "World"));
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(6, "World"));
+        sb.insert(5, true);
+        assertEquals("Hellotrue", sb.toString());
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(10, false));
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(-20, false));
+        sb.insert(9, 'A');
+        assertEquals("HellotrueA", sb.toString());
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(11, 'B'));
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(-2, 'B'));
+        sb.insert(10, new char[] { 'B', 'C' });
+        assertEquals("HellotrueABC", sb.toString());
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(13, new char[] { 'D', 'E' }));
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(-1, new char[] { 'D', 'E' }));
+        sb.insert(12, new char[] { 'D', 'E', 'F' }, 1, 1);
+        assertEquals("HellotrueABCE", sb.toString());
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(14, new char[] { 'G', 'H', 'I' }, 1, 2));
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(-1, new char[] { 'G', 'H', 'I' }, 1, 1));
+        sb.insert(13, 1.2d);
+        assertEquals("HellotrueABCE1.2", sb.toString());
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(17, 1.3d));
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(-1, 1.3d));
+        sb.insert(16, 1f);
+        assertEquals("HellotrueABCE1.21.0", sb.toString());
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(20, 1.3f));
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(-3, 1.3f));
+        sb.insert(19, 23);
+        assertEquals("HellotrueABCE1.21.023", sb.toString());
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(22, 20));
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(-5, -5));
+        sb.insert(21, 99L);
+        assertEquals("HellotrueABCE1.21.02399", sb.toString());
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(24, 22L));
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(-1, -1L));
+        sb.insert(23, FOO);
+        assertEquals("HellotrueABCE1.21.02399foo", sb.toString());
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(27, FOO));
+        assertThrows(IndexOutOfBoundsException.class, () -> sb.insert(-3, FOO));
+    }
+
     @Test
     void testInsertWithNullText() {
         final TextStringBuilder sb = new TextStringBuilder();