diff --git a/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java b/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java index 9fc1c212920..e56dab394a0 100644 --- a/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java +++ b/luni/src/test/java/org/apache/harmony/regex/tests/java/util/regex/PatternTest.java @@ -33,6 +33,11 @@ import static java.util.Arrays.asList; +import android.compat.Compatibility; + +import dalvik.annotation.compat.VersionCodes; +import dalvik.system.VMRuntime; + public class PatternTest extends TestCaseWithRules { @Rule public TestRule switchTargetSdkVersionRule = SwitchTargetSdkVersionRule.getInstance(); @@ -2199,16 +2204,13 @@ public void testSplitAsStream() { pat = Pattern.compile("b"); s = pat.splitAsStream("").toArray(String[]::new); - // The length is 1 because the javadoc says "If this pattern does not match any subsequence - // of the input then the resulting stream has just one element, namely the input sequence - // in string form. - assertEquals(1, s.length); - assertEquals(s[0], ""); + assertEquals(getExpectedEmptyStringSplitLength(), s.length); + checkContainsOnlyEmptyStrings(s); pat = Pattern.compile(""); s = pat.splitAsStream("").toArray(String[]::new); - assertEquals(1, s.length); - assertEquals(s[0], ""); + assertEquals(getExpectedEmptyStringSplitLength(), s.length); + checkContainsOnlyEmptyStrings(s); pat = Pattern.compile(""); s = pat.splitAsStream("abccbadfe").toArray(String[]::new); @@ -2216,4 +2218,23 @@ public void testSplitAsStream() { assertEquals(s[0], "a"); assertEquals(s[8], "e"); } + + private int getExpectedEmptyStringSplitLength() { + if (VMRuntime.getSdkVersion() >= VersionCodes.UPSIDE_DOWN_CAKE + && Compatibility.isChangeEnabled( + Pattern.SPLIT_AS_STREAM_RETURNS_SINGLE_EMPTY_STRING)) { + // The length is 1 because the javadoc says "If this pattern does not match any + // subsequence of the input then the resulting stream has just one element, + // namely the input sequence in string form. + return 1; + } else { + return 0; + } + } + + private void checkContainsOnlyEmptyStrings(String[] sArray) { + for (String s : sArray) { + assertEquals(s, ""); + } + } } diff --git a/ojluni/src/main/java/java/util/regex/Pattern.java b/ojluni/src/main/java/java/util/regex/Pattern.java index 1caa4aeecf7..43fa23389df 100644 --- a/ojluni/src/main/java/java/util/regex/Pattern.java +++ b/ojluni/src/main/java/java/util/regex/Pattern.java @@ -26,7 +26,13 @@ package java.util.regex; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; + import com.android.icu.util.regex.PatternNative; + +import dalvik.annotation.compat.VersionCodes; import dalvik.system.VMRuntime; import java.util.ArrayList; import java.util.Iterator; @@ -5731,7 +5737,16 @@ public boolean hasNext() { // If the input is an empty string then the result can only be a // stream of the input. Induce that by setting the empty // element count to 1 - emptyElementCount = input.length() == 0 ? 1 : 0; + // Android-changed: Keep old behavior on Android 13 or below. http://b/286499139 + // emptyElementCount = input.length() == 0 ? 1 : 0; + if (input.length() == 0 + && VMRuntime.getSdkVersion() >= VersionCodes.UPSIDE_DOWN_CAKE + && Compatibility.isChangeEnabled( + SPLIT_AS_STREAM_RETURNS_SINGLE_EMPTY_STRING)) { + emptyElementCount = 1; + } else { + emptyElementCount = 0; + } } if (nextElement != null || emptyElementCount > 0) return true; @@ -5768,4 +5783,19 @@ public boolean hasNext() { return StreamSupport.stream(Spliterators.spliteratorUnknownSize( new MatcherIterator(), Spliterator.ORDERED | Spliterator.NONNULL), false); } + + // Android-added: Backward-compatible flag for splitAsStream() API. + /** + * Since Android 14, {@link Pattern#splitAsStream(CharSequence)} return a stream of a single + * empty String as described in the API documentation. Previously, given an empty string input, + * the method returns an empty stream. + * + * This flag is enabled for apps targeting Android 14+. + * + * @hide + */ + @ChangeId + @EnabledSince(targetSdkVersion = VersionCodes.UPSIDE_DOWN_CAKE) + public static final long SPLIT_AS_STREAM_RETURNS_SINGLE_EMPTY_STRING = 288845345L; + } diff --git a/ojluni/src/test/java/util/regex/PatternStreamTest.java b/ojluni/src/test/java/util/regex/PatternStreamTest.java index 74ff21b4316..edbd0541c98 100644 --- a/ojluni/src/test/java/util/regex/PatternStreamTest.java +++ b/ojluni/src/test/java/util/regex/PatternStreamTest.java @@ -37,6 +37,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -52,6 +53,13 @@ import static org.testng.Assert.*; +import static java.util.regex.Pattern.SPLIT_AS_STREAM_RETURNS_SINGLE_EMPTY_STRING; + +import android.compat.Compatibility; + +import dalvik.annotation.compat.VersionCodes; +import dalvik.system.VMRuntime; + @Test public class PatternStreamTest extends OpTestCase { @@ -139,6 +147,14 @@ public void testPatternSplitAsStream(String description, String input, Pattern p // Derive expected result from pattern.split List expected = Arrays.asList(pattern.split(input)); + // Android-added: Keep old behavior on Android 13 or below. http://b/286499139 + if(input.isEmpty() + && !(VMRuntime.getSdkVersion() >= VersionCodes.UPSIDE_DOWN_CAKE + && Compatibility.isChangeEnabled( + Pattern.SPLIT_AS_STREAM_RETURNS_SINGLE_EMPTY_STRING))) { + expected = Collections.emptyList(); + } + Supplier> ss = () -> pattern.splitAsStream(input); withData(TestData.Factory.ofSupplier(description, ss)) .stream(LambdaTestHelpers.identity())