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 <xenoam...@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 + ); + } }