Skip to content

Commit

Permalink
common: Add support for indexOf
Browse files Browse the repository at this point in the history
  • Loading branch information
kohlschuetter committed Dec 31, 2023
1 parent 0c907f6 commit 94dafde
Show file tree
Hide file tree
Showing 2 changed files with 264 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.PrimitiveIterator;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
import java.util.function.Supplier;
Expand Down Expand Up @@ -711,8 +712,106 @@ default boolean isCacheable() {

/**
* Deep-clones this {@link StringHolder}.
*
*
* @return The cloned instance.
*/
StringHolder clone();

/**
* Returns the index within this string of the first occurrence of the specified character, or
* {@code -1} if not found.
*
* @param c The character/codepoint to look for.
* @return The position, or {@code -1} if not found.
*/
default int indexOf(int c) {
if (isString()) {
return toString().indexOf(c);
} else if (isKnownEmpty()) {
return -1;
}

int i = 0;
for (PrimitiveIterator.OfInt it = chars().iterator(); it.hasNext(); i++) {
int ch = it.next();
if (ch == c) {
return i;
}
}
return -1;
}

/**
* Returns the index within this StringHolder of the first occurrence of the specified
* CharSequence, or {@code -1} if not found.
*
* @param str The char sequence to look for.
* @return The position, or {@code -1} if not found.
*/
@SuppressWarnings("PMD.CognitiveComplexity")
default int indexOf(CharSequence str) {
if (str == this) { // NOPMD.CompareObjectsWithEquals
return 0;
} else if (isString()) {
if (str instanceof String || //
(str instanceof StringHolder && ((StringHolder) str).isString())) {
return toString().indexOf(str.toString());
}
}

if (CharSequenceReleaseShim.isEmpty(str)) {
return 0;
} else if (isKnownEmpty()) {
return -1;
}

int strLen = str.length();
if (isLengthKnown() && length() < strLen) {
return -1;
}

char firstChar = str.charAt(0);
if (str.length() == 1) {
return indexOf(firstChar);
}

int max = length() - strLen;

boolean found = false;
loop : for (int i = 0; i <= max; i++) {
char myChar = charAt(i);
if (myChar != firstChar) {
// seek ahead
while (++i <= max) { // NOPMD.AvoidReassigningLoopVariables
if (charAt(i) == firstChar) {
found = true;
break;
}
}
} else {
found = true;
}
if (found) {
int myPos = i + 1;
int end = myPos + strLen - 1;
for (int strPos = 1; myPos < end; myPos++, strPos++) {
if (charAt(myPos) != str.charAt(strPos)) {
continue loop;
}
}
return i;
}
}
return -1;
}

/**
* Checks if this {@link StringHolder} contains the given {@link CharSequence}.
*
* @param s The char sequence to look for.
* @return {@code true} if found (also if the sequence is empty).
*/
default boolean contains(CharSequence s) {
return indexOf(s) >= 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1270,4 +1270,168 @@ protected String getString() {
sh.markEffectivelyImmutable();
assertTrue(sh.isEffectivelyImmutable());
}

private static StringHolder emptyStringHolderButNotAString(boolean lengthKnown) {
return new AbstractStringHolder() {

@Override
public boolean isLengthKnown() {
return lengthKnown;
}

@Override
protected int computeLength() {
return 0;
}

@Override
protected String getString() {
return "";
}
};
}

@Test
public void testIndexOfChar() throws Exception {
assertEquals(0, StringHolder.withContent("Foo bar").indexOf('F'));
assertEquals(3, StringHolder.withContent("Foo bar").indexOf(' '));
assertEquals(6, StringHolder.withContent("Foo bar").indexOf('r'));
assertEquals(-1, StringHolder.withContent("Foo bar").indexOf('f'));
assertEquals(0, StringHolder.withContent("F").indexOf('F'));
assertEquals(0, StringHolder.withSupplier(() -> "F").indexOf('F'));

assertEquals(-1, StringHolder.withContent("").indexOf(' '));
assertEquals(-1, StringHolder.withContent(new StringBuilder()).indexOf(' '));
assertEquals(-1, StringHolder.withSupplierFixedLength(0, () -> "").indexOf(' '));
assertEquals(-1, emptyStringHolderButNotAString(false).indexOf(' '));
assertEquals(-1, emptyStringHolderButNotAString(true).indexOf(' '));

assertEquals(3, StringHolder.withSupplier(() -> "Foo bar").indexOf(' '));
assertEquals(3, StringHolder.newSequence().append(StringHolder.withContent("Foo")).append(' ')
.append(StringHolder.withSupplier(() -> "bar")).indexOf(' '));
}

@Test
public void testIndexOfCharSequenceSingleChar() throws Exception {
assertEquals(0, StringHolder.withContent("Foo bar").indexOf("F"));
assertEquals(3, StringHolder.withContent("Foo bar").indexOf(" "));
assertEquals(6, StringHolder.withContent("Foo bar").indexOf("r"));
assertEquals(-1, StringHolder.withContent("Foo bar").indexOf("f"));
assertEquals(0, StringHolder.withContent("F").indexOf("F"));
assertEquals(0, StringHolder.withSupplier(() -> "F").indexOf("F"));

assertEquals(-1, StringHolder.withContent("").indexOf(" "));
assertEquals(-1, StringHolder.withContent(new StringBuilder()).indexOf(" "));
assertEquals(-1, StringHolder.withSupplierFixedLength(0, () -> "").indexOf(" "));
assertEquals(-1, emptyStringHolderButNotAString(false).indexOf(" "));
assertEquals(-1, emptyStringHolderButNotAString(true).indexOf(" "));

assertEquals(3, StringHolder.withSupplier(() -> "Foo bar").indexOf(" "));
assertEquals(3, StringHolder.newSequence().append(StringHolder.withContent("Foo")).append(" ")
.append(StringHolder.withSupplier(() -> "bar")).indexOf(" "));
}

@Test
public void testIndexOfEmptyCharSequence() throws Exception {
assertEquals(0, "".indexOf(""));
assertEquals(0, "Foo bar".indexOf(""));

assertEquals(0, StringHolder.withContent("Foo bar").indexOf(""));
assertEquals(0, StringHolder.withContent("Foo bar").indexOf(new CharSequence() {

@Override
public CharSequence subSequence(int start, int end) {
return this;
}

@Override
public int length() {
return 0;
}

@Override
public char charAt(int index) {
return (char) -1;
}
}));

assertEquals(0, StringHolder.withContent("").indexOf(""));
assertEquals(0, StringHolder.withContent(new StringBuilder()).indexOf(""));
assertEquals(0, StringHolder.withSupplierFixedLength(0, () -> "").indexOf(""));
assertEquals(0, emptyStringHolderButNotAString(false).indexOf(""));
assertEquals(0, emptyStringHolderButNotAString(true).indexOf(""));

assertEquals(0, StringHolder.newSequence().indexOf(""));
assertEquals(0, StringHolder.newSequence().append(StringHolder.withContent("")).indexOf(""));
}

@Test
public void testIndexOfMissing() throws Exception {
assertEquals(-1, "".indexOf("bar Foo"));
assertEquals(-1, "Foo bar".indexOf("bar Foo"));

assertEquals(-1, StringHolder.withContent("Foo bar").indexOf("bar Foo"));
assertEquals(-1, StringHolder.withContent("Foo bar").indexOf(new StringBuilder("bar Foo")));

assertEquals(-1, StringHolder.withContent("Foo bar").indexOf("bar Foo"));
assertEquals(-1, StringHolder.withContent(new StringBuilder("Foo bar")).indexOf("bar Foo"));
assertEquals(-1, StringHolder.withSupplierFixedLength(0, () -> "Foo bar").indexOf("bar Foo"));
assertEquals(-1, StringHolder.withSupplierFixedLength(3, () -> "Foo").indexOf("bar Foo"));
assertEquals(-1, StringHolder.withSupplierFixedLength(3, () -> "Foo").indexOf(StringHolder
.withSupplierFixedLength(7, () -> "bar Foo")));

assertEquals(-1, StringHolder.newSequence().append("Foo bar").indexOf("bar Foo"));
assertEquals(-1, StringHolder.newSequence().append(StringHolder.withContent("Foo")).append(" ")
.append(StringHolder.withSupplier(() -> "bar")).indexOf("bar Foo"));
}

@Test
public void testIndexOfFound() throws Exception {
assertEquals(4, "Foo bar".indexOf("bar"));

assertEquals(4, StringHolder.withContent("Foo bar").indexOf("bar"));
assertEquals(4, StringHolder.withContent("Foo bar").indexOf(new StringBuilder("bar")));
assertEquals(4, StringHolder.withContent("Foo bar").indexOf(StringHolder
.withSupplierFixedLength(3, () -> "bar")));

StringHolder sh = StringHolder.withContent("Foo bar");
sh.toString();
assertEquals(4, sh.indexOf("bar"));
assertEquals(4, sh.indexOf(StringHolder.withContent("bar")));

assertEquals(4, StringHolder.withContent("Foo bar").indexOf("bar"));
assertEquals(4, StringHolder.withContent(new StringBuilder("Foo bar")).indexOf("bar"));

assertEquals(4, StringHolder.withSupplierFixedLength(7, () -> "Foo bar").indexOf("bar"));
assertEquals(-1, StringHolder.withSupplierFixedLength(0, () -> "Foo bar").indexOf("bar"));

assertEquals(4, StringHolder.newSequence().append("Foo bar").indexOf("bar"));
assertEquals(4, StringHolder.newSequence().append(StringHolder.withContent("Foo")).append(" ")
.append(StringHolder.withSupplier(() -> "bar")).indexOf("bar"));
}

@Test
public void testIndexOfSelf() throws Exception {
StringHolder sh;

sh = StringHolder.withContent("Foo bar");
assertEquals(0, sh.indexOf(sh));

sh = StringHolder.withContent("");
assertEquals(0, sh.indexOf(sh));

sh = StringHolder.withSupplier(() -> "");
assertEquals(0, sh.indexOf(sh));
}

@Test
public void testContains() throws Exception {
assertTrue(StringHolder.withContent("Foo bar").contains("bar"));
assertFalse(StringHolder.withContent("Foo bar").contains("baz"));
}

@Test
public void testIndexOfPartialMatch() {
assertEquals(4, StringHolder.withContent("Foo Fobar").indexOf(new StringBuilder("Fobar")));
}
}

0 comments on commit 94dafde

Please sign in to comment.