You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ch...@apache.org on 2020/06/26 09:55:23 UTC

[commons-lang] 04/16: implement lastIndexOf

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

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

commit dee8f6fb849fd3b7d928f13e273f8160f0a45f5b
Author: XenoAmess <xe...@gmail.com>
AuthorDate: Mon Jun 1 01:51:43 2020 +0800

    implement lastIndexOf
---
 .../apache/commons/lang3/CharSequenceUtils.java    | 78 ++++++++++++++----
 .../commons/lang3/CharSequenceUtilsTest.java       | 95 ++++++++++++++++++++++
 2 files changed, 156 insertions(+), 17 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java b/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java
index 0d9db8e..4dab063 100644
--- a/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java
+++ b/src/main/java/org/apache/commons/lang3/CharSequenceUtils.java
@@ -216,6 +216,8 @@ public class CharSequenceUtils {
         return NOT_FOUND;
     }
 
+    static final int TO_STRING_LIMIT = 16;
+
     /**
      * Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf
      *
@@ -224,24 +226,66 @@ public class CharSequenceUtils {
      * @param start the start index
      * @return the index where the search sequence was found
      */
-    static int lastIndexOf(final CharSequence cs, final CharSequence searchChar, final int start) {
-        if (cs instanceof String) {
-            return ((String) cs).lastIndexOf((String) searchChar, start);
-        } else if (cs instanceof StringBuilder) {
-            return ((StringBuilder) cs).lastIndexOf((String) searchChar, start);
-        } else if (cs instanceof StringBuffer) {
-            return ((StringBuffer) cs).lastIndexOf((String) searchChar, start);
+    static int lastIndexOf(final CharSequence cs, final CharSequence searchChar, int start) {
+        if (searchChar instanceof String) {
+            if (cs instanceof String) {
+                return ((String) cs).lastIndexOf((String) searchChar, start);
+            } else if (cs instanceof StringBuilder) {
+                return ((StringBuilder) cs).lastIndexOf((String) searchChar, start);
+            } else if (cs instanceof StringBuffer) {
+                return ((StringBuffer) cs).lastIndexOf((String) searchChar, start);
+            }
         }
-        return cs.toString().lastIndexOf(searchChar.toString(), start);
-//        if (cs instanceof String && searchChar instanceof String) {
-//            // TODO: Do we assume searchChar is usually relatively small;
-//            //       If so then calling toString() on it is better than reverting to
-//            //       the green implementation in the else block
-//            return ((String) cs).lastIndexOf((String) searchChar, start);
-//        } else {
-//            // TODO: Implement rather than convert to String
-//            return cs.toString().lastIndexOf(searchChar.toString(), start);
-//        }
+
+        int len1 = cs.length();
+        int len2 = searchChar.length();
+
+        if (start > len1) {
+            start = len1;
+        }
+
+        if (start < 0 || len2 < 0 || len2 > len1) {
+            return -1;
+        }
+
+        if (len2 == 0) {
+            return start;
+        }
+
+        if (len2 <= TO_STRING_LIMIT) {
+            if (cs instanceof String) {
+                return ((String) cs).lastIndexOf(searchChar.toString(), start);
+            } else if (cs instanceof StringBuilder) {
+                return ((StringBuilder) cs).lastIndexOf(searchChar.toString(), start);
+            } else if (cs instanceof StringBuffer) {
+                return ((StringBuffer) cs).lastIndexOf(searchChar.toString(), start);
+            }
+        }
+
+        if (start + len2 > len1) {
+            start = len1 - len2;
+        }
+
+        if (check(cs, searchChar, len2, start)) {
+            return start;
+        }
+
+        for (int i = start - 1; i >= 0; i--) {
+            if (check(cs, searchChar, len2, i)) {
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    private static boolean check(final CharSequence cs, final CharSequence searchChar, int len2, int start1) {
+        for (int i = 0; i < len2; i++) {
+            if (cs.charAt(start1 + i) != searchChar.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/lang3/CharSequenceUtilsTest.java b/src/test/java/org/apache/commons/lang3/CharSequenceUtilsTest.java
index e7dd3b4..c230c45 100644
--- a/src/test/java/org/apache/commons/lang3/CharSequenceUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/CharSequenceUtilsTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.commons.lang3;
 
+import static java.nio.CharBuffer.wrap;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -26,7 +27,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
+import java.nio.CharBuffer;
+import java.util.Random;
+import java.util.stream.IntStream;
 
+import org.apache.commons.lang3.text.StrBuilder;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -186,4 +191,94 @@ public class CharSequenceUtilsTest {
         assertArrayEquals(expected, CharSequenceUtils.toCharArray(builder.toString()));
     }
 
+    static class WrapperString implements CharSequence {
+        CharSequence inner;
+
+        public WrapperString(CharSequence inner) {
+            this.inner = inner;
+        }
+
+        @Override
+        public int length() {
+            return inner.length();
+        }
+
+        @Override
+        public char charAt(int index) {
+            return inner.charAt(index);
+        }
+
+        @Override
+        public CharSequence subSequence(int start, int end) {
+            return inner.subSequence(start, end);
+        }
+
+        @Override
+        public String toString() {
+            return inner.toString();
+        }
+
+        @Override
+        public IntStream chars() {
+            return inner.chars();
+        }
+
+        @Override
+        public IntStream codePoints() {
+            return inner.codePoints();
+        }
+    }
+
+    @Test
+    public void testNewLastIndexOf() {
+        testNewLastIndexOfSingle("808087847-1321060740-635567660180086727-925755305", "-1321060740-635567660", 21);
+        testNewLastIndexOfSingle("", "");
+        testNewLastIndexOfSingle("1", "");
+        testNewLastIndexOfSingle("", "1");
+        testNewLastIndexOfSingle("1", "1");
+        testNewLastIndexOfSingle("11", "1");
+        testNewLastIndexOfSingle("1", "11");
+
+        Random random = new Random();
+        StringBuilder seg = new StringBuilder();
+        while (seg.length() <= CharSequenceUtils.TO_STRING_LIMIT) {
+            seg.append(random.nextInt());
+        }
+        StringBuilder original = new StringBuilder(seg);
+        testNewLastIndexOfSingle(original, seg);
+        for (int i = 0; i < 100; i++) {
+            if (random.nextDouble() < 0.5) {
+                original.append(random.nextInt() % 10);
+            } else {
+                original = new StringBuilder().append(String.valueOf(random.nextInt() % 100)).append(original);
+            }
+            testNewLastIndexOfSingle(original, seg);
+        }
+    }
+
+    private void testNewLastIndexOfSingle(CharSequence a, CharSequence b) {
+        int maxa = Math.max(a.length(), b.length());
+        for (int i = -maxa-10; i <= maxa+10; i++) {
+            testNewLastIndexOfSingle(a, b, i);
+        }
+    }
+
+    private void testNewLastIndexOfSingle(CharSequence a, CharSequence b, int start) {
+        testNewLastIndexOfSingleSingle(a, b, start);
+        testNewLastIndexOfSingleSingle(b, a, start);
+    }
+
+    private void testNewLastIndexOfSingleSingle(CharSequence a, CharSequence b, int start) {
+        int expected = a.toString().lastIndexOf(b.toString(), start);
+//        assertEquals(
+//                expected,
+//                lastIndexOf(new WrapperString(a), b, start),
+//                "testNewLastIndexOf fails! original : " + a + " seg : " + b + " start : " + start
+//        );
+        assertEquals(
+                expected,
+                CharSequenceUtils.lastIndexOf(new WrapperString(a.toString()), b.toString(), start),
+                "testNewLastIndexOf fails! original : " + a + " seg : " + b + " start : " + start
+        );
+    }
 }