diff --git a/community/random-values/src/test/java/org/neo4j/values/storable/TextValueFuzzTest.java b/community/random-values/src/test/java/org/neo4j/values/storable/TextValueFuzzTest.java index dea24286c0ae9..ee7b187b26fe9 100644 --- a/community/random-values/src/test/java/org/neo4j/values/storable/TextValueFuzzTest.java +++ b/community/random-values/src/test/java/org/neo4j/values/storable/TextValueFuzzTest.java @@ -98,7 +98,7 @@ void shouldTrim() } @Test - void shouldHandleStartsWithAndEndsWith() + void shouldHandleStringPredicates() { for ( int i = 0; i < ITERATIONS; i++ ) { @@ -115,6 +115,7 @@ void shouldHandleStartsWithAndEndsWith() assertConsistent( value, other, TextValue::startsWith ); assertConsistent( value, other, TextValue::endsWith ); + assertConsistent( value, other, TextValue::contains ); } } diff --git a/community/values/src/main/java/org/neo4j/values/storable/CharValue.java b/community/values/src/main/java/org/neo4j/values/storable/CharValue.java index 87aacc330e7d9..a6d166e5719d1 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/CharValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/CharValue.java @@ -211,6 +211,12 @@ public boolean endsWith( TextValue other ) return startsWith( other ); } + @Override + public boolean contains( TextValue other ) + { + return startsWith( other ); + } + public char value() { return value; diff --git a/community/values/src/main/java/org/neo4j/values/storable/StringValue.java b/community/values/src/main/java/org/neo4j/values/storable/StringValue.java index 4836efb82599f..dd8aab4b83191 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/StringValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/StringValue.java @@ -245,6 +245,12 @@ public boolean endsWith( TextValue other ) return other.length() == 0; } + @Override + public boolean contains( TextValue other ) + { + return other.length() == 0; + } + @Override public TextValue toLower() { diff --git a/community/values/src/main/java/org/neo4j/values/storable/StringWrappingStringValue.java b/community/values/src/main/java/org/neo4j/values/storable/StringWrappingStringValue.java index 7585c3927ccb1..82b7289aaa71f 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/StringWrappingStringValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/StringWrappingStringValue.java @@ -153,6 +153,12 @@ public boolean endsWith( TextValue other ) return value.endsWith( other.stringValue() ); } + @Override + public boolean contains( TextValue other ) + { + return value.contains( other.stringValue() ); + } + @Override Matcher matcher( Pattern pattern ) { diff --git a/community/values/src/main/java/org/neo4j/values/storable/TextValue.java b/community/values/src/main/java/org/neo4j/values/storable/TextValue.java index 737f420e6fa00..28cf0a2c3fa00 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/TextValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/TextValue.java @@ -74,6 +74,8 @@ public TextValue substring( int start ) public abstract boolean endsWith( TextValue other ); + public abstract boolean contains( TextValue other ); + public abstract int compareTo( TextValue other ); @Override diff --git a/community/values/src/main/java/org/neo4j/values/storable/UTF8StringValue.java b/community/values/src/main/java/org/neo4j/values/storable/UTF8StringValue.java index 5d37655b93675..8a099180905a7 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/UTF8StringValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/UTF8StringValue.java @@ -363,6 +363,55 @@ public boolean endsWith( TextValue other ) return value().endsWith( other.stringValue() ); } + @Override + public boolean contains( TextValue other ) + { + + if ( other instanceof UTF8StringValue ) + { + final UTF8StringValue substring = (UTF8StringValue) other; + if ( byteLength == 0 ) + { + return substring.byteLength == 0; + } + if ( substring.byteLength == 0 ) + { + return true; + } + if ( substring.byteLength > byteLength ) + { + return false; + } + + final byte first = substring.bytes[substring.offset]; + int max = offset + byteLength - substring.byteLength; + for ( int pos = offset; pos <= max; pos++ ) + { + //find first byte + if ( bytes[pos] != first ) + { + while ( ++pos <= max && bytes[pos] != first ); + } + + //Now we have the first byte match, look at the rest + if ( pos <= max ) + { + int i = pos + 1; + final int end = pos + substring.byteLength; + for ( int j = substring.offset + 1; i < end && bytes[i] == substring.bytes[j]; j++, i++); + + if ( i == end ) + { + return true; + } + } + } + return false; + } + + return value().contains( other.stringValue() ); + } + private boolean startsWith(UTF8StringValue prefix, int startPos) { int thisOffset = offset+ startPos; diff --git a/community/values/src/test/java/org/neo4j/values/storable/UTF8StringValueTest.java b/community/values/src/test/java/org/neo4j/values/storable/UTF8StringValueTest.java index 230f60943d055..c706798564e26 100644 --- a/community/values/src/test/java/org/neo4j/values/storable/UTF8StringValueTest.java +++ b/community/values/src/test/java/org/neo4j/values/storable/UTF8StringValueTest.java @@ -230,7 +230,7 @@ void shouldThrowOnNegativeLength() } @Test - void shouldHandleStartsWithAndEndsWithWhenOffset() + void shouldHandleStringPredicatesWithOffset() { // Given byte[] bytes = "abcdefghijklmnoprstuvxyzABCDEFGHIJKLMNOPRSTUVXYZ".getBytes( UTF_8 ); @@ -249,11 +249,15 @@ void shouldHandleStartsWithAndEndsWithWhenOffset() assertThat( value.startsWith( other ), equalTo( otherLength == 0 || otherOffset == offset && otherLength <= length ) ); assertThat( value.endsWith( other ), - equalTo( otherLength == 0 || otherOffset >= offset && otherLength == length + offset - otherOffset ) ); + equalTo( otherLength == 0 || + otherOffset >= offset && otherLength == length + offset - otherOffset ) ); + assertThat( value.contains( other ), + equalTo( otherLength == 0 || + otherOffset >= offset && otherLength <= length + offset - otherOffset ) ); + } } - } } }