diff --git a/jdk/src/java.base/share/classes/java/util/regex/Matcher.java b/jdk/src/java.base/share/classes/java/util/regex/Matcher.java index 9fe1b9c5d60..0fcd3eac11f 100644 --- a/jdk/src/java.base/share/classes/java/util/regex/Matcher.java +++ b/jdk/src/java.base/share/classes/java/util/regex/Matcher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,16 @@ package java.util.regex; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.NoSuchElementException; import java.util.Objects; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * An engine that performs match operations on a {@linkplain java.lang.CharSequence @@ -208,6 +217,11 @@ public final class Matcher implements MatchResult { */ boolean anchoringBounds = true; + /** + * Number of times this matcher's state has been modified + */ + int modCount; + /** * No default constructor. */ @@ -248,11 +262,76 @@ public final class Matcher implements MatchResult { * @since 1.5 */ public MatchResult toMatchResult() { - Matcher result = new Matcher(this.parentPattern, text.toString()); - result.first = this.first; - result.last = this.last; - result.groups = this.groups.clone(); - return result; + return toMatchResult(text.toString()); + } + + private MatchResult toMatchResult(String text) { + return new ImmutableMatchResult(this.first, + this.last, + groupCount(), + this.groups.clone(), + text); + } + + private static class ImmutableMatchResult implements MatchResult { + private final int first; + private final int last; + private final int[] groups; + private final int groupCount; + private final String text; + + ImmutableMatchResult(int first, int last, int groupCount, + int groups[], String text) + { + this.first = first; + this.last = last; + this.groupCount = groupCount; + this.groups = groups; + this.text = text; + } + + @Override + public int start() { + return first; + } + + @Override + public int start(int group) { + if (group < 0 || group > groupCount) + throw new IndexOutOfBoundsException("No group " + group); + return groups[group * 2]; + } + + @Override + public int end() { + return last; + } + + @Override + public int end(int group) { + if (group < 0 || group > groupCount) + throw new IndexOutOfBoundsException("No group " + group); + return groups[group * 2 + 1]; + } + + @Override + public int groupCount() { + return groupCount; + } + + @Override + public String group() { + return group(0); + } + + @Override + public String group(int group) { + if (group < 0 || group > groupCount) + throw new IndexOutOfBoundsException("No group " + group); + if ((groups[group*2] == -1) || (groups[group*2+1] == -1)) + return null; + return text.subSequence(groups[group * 2], groups[group * 2 + 1]).toString(); + } } /** @@ -284,6 +363,7 @@ public final class Matcher implements MatchResult { groups[i] = -1; for (int i = 0; i < locals.length; i++) locals[i] = -1; + modCount++; return this; } @@ -308,6 +388,7 @@ public final class Matcher implements MatchResult { lastAppendPosition = 0; from = 0; to = getTextLength(); + modCount++; return this; } @@ -803,6 +884,7 @@ public final class Matcher implements MatchResult { // Append the match substitution sb.append(result); lastAppendPosition = last; + modCount++; return this; } @@ -892,6 +974,7 @@ public final class Matcher implements MatchResult { // Append the match substitution sb.append(result); lastAppendPosition = last; + modCount++; return this; } @@ -1077,6 +1160,183 @@ public final class Matcher implements MatchResult { return text.toString(); } + /** + * Replaces every subsequence of the input sequence that matches the + * pattern with the result of applying the given replacer function to the + * match result of this matcher corresponding to that subsequence. + * Exceptions thrown by the function are relayed to the caller. + * + *

This method first resets this matcher. It then scans the input + * sequence looking for matches of the pattern. Characters that are not + * part of any match are appended directly to the result string; each match + * is replaced in the result by the applying the replacer function that + * returns a replacement string. Each replacement string may contain + * references to captured subsequences as in the {@link #appendReplacement + * appendReplacement} method. + * + *

Note that backslashes (\) and dollar signs ($) in + * a replacement string may cause the results to be different than if it + * were being treated as a literal replacement string. Dollar signs may be + * treated as references to captured subsequences as described above, and + * backslashes are used to escape literal characters in the replacement + * string. + * + *

Given the regular expression dog, the input + * "zzzdogzzzdogzzz", and the function + * mr -> mr.group().toUpperCase(), an invocation of this method on + * a matcher for that expression would yield the string + * "zzzDOGzzzDOGzzz". + * + *

Invoking this method changes this matcher's state. If the matcher + * is to be used in further matching operations then it should first be + * reset.

+ * + *

The replacer function should not modify this matcher's state during + * replacement. This method will, on a best-effort basis, throw a + * {@link java.util.ConcurrentModificationException} if such modification is + * detected. + * + *

The state of each match result passed to the replacer function is + * guaranteed to be constant only for the duration of the replacer function + * call and only if the replacer function does not modify this matcher's + * state. + * + * @implNote + * This implementation applies the replacer function to this matcher, which + * is an instance of {@code MatchResult}. + * + * @param replacer + * The function to be applied to the match result of this matcher + * that returns a replacement string. + * @return The string constructed by replacing each matching subsequence + * with the result of applying the replacer function to that + * matched subsequence, substituting captured subsequences as + * needed. + * @throws NullPointerException if the replacer function is null + * @throws ConcurrentModificationException if it is detected, on a + * best-effort basis, that the replacer function modified this + * matcher's state + * @since 1.9 + */ + public String replaceAll(Function replacer) { + Objects.requireNonNull(replacer); + reset(); + boolean result = find(); + if (result) { + StringBuilder sb = new StringBuilder(); + do { + int ec = modCount; + String replacement = replacer.apply(this); + if (ec != modCount) + throw new ConcurrentModificationException(); + appendReplacement(sb, replacement); + result = find(); + } while (result); + appendTail(sb); + return sb.toString(); + } + return text.toString(); + } + + /** + * Returns a stream of match results for each subsequence of the input + * sequence that matches the pattern. The match results occur in the + * same order as the matching subsequences in the input sequence. + * + *

Each match result is produced as if by {@link #toMatchResult()}. + * + *

This method does not reset this matcher. Matching starts on + * initiation of the terminal stream operation either at the beginning of + * this matcher's region, or, if the matcher has not since been reset, at + * the first character not matched by a previous match. + * + *

If the matcher is to be used for further matching operations after + * the terminal stream operation completes then it should be first reset. + * + *

This matcher's state should not be modified during execution of the + * returned stream's pipeline. The returned stream's source + * {@code Spliterator} is fail-fast and will, on a best-effort + * basis, throw a {@link java.util.ConcurrentModificationException} if such + * modification is detected. + * + * @return a sequential stream of match results. + * @since 1.9 + */ + public Stream results() { + class MatchResultIterator implements Iterator { + // -ve for call to find, 0 for not found, 1 for found + int state = -1; + // State for concurrent modification checking + // -1 for uninitialized + int expectedCount = -1; + // The input sequence as a string, set once only after first find + // Avoids repeated conversion from CharSequence for each match + String textAsString; + + @Override + public MatchResult next() { + if (expectedCount >= 0 && expectedCount != modCount) + throw new ConcurrentModificationException(); + + if (!hasNext()) + throw new NoSuchElementException(); + + state = -1; + return toMatchResult(textAsString); + } + + @Override + public boolean hasNext() { + if (state >= 0) + return state == 1; + + // Defer throwing ConcurrentModificationException to when next + // or forEachRemaining is called. The is consistent with other + // fail-fast implementations. + if (expectedCount >= 0 && expectedCount != modCount) + return true; + + boolean found = find(); + // Capture the input sequence as a string on first find + if (found && state < 0) + textAsString = text.toString(); + state = found ? 1 : 0; + expectedCount = modCount; + return found; + } + + @Override + public void forEachRemaining(Consumer action) { + if (expectedCount >= 0 && expectedCount != modCount) + throw new ConcurrentModificationException(); + + int s = state; + if (s == 0) + return; + + // Set state to report no more elements on further operations + state = 0; + expectedCount = -1; + + // Perform a first find if required + if (s < 0 && !find()) + return; + + // Capture the input sequence as a string on first find + textAsString = text.toString(); + + do { + int ec = modCount; + action.accept(toMatchResult(textAsString)); + if (ec != modCount) + throw new ConcurrentModificationException(); + } while (find()); + } + } + return StreamSupport.stream(Spliterators.spliteratorUnknownSize( + new MatchResultIterator(), Spliterator.ORDERED | Spliterator.NONNULL), false); + } + /** * Replaces the first subsequence of the input sequence that matches the * pattern with the given replacement string. @@ -1122,6 +1382,79 @@ public final class Matcher implements MatchResult { return sb.toString(); } + /** + * Replaces the first subsequence of the input sequence that matches the + * pattern with the result of applying the given replacer function to the + * match result of this matcher corresponding to that subsequence. + * Exceptions thrown by the replace function are relayed to the caller. + * + *

This method first resets this matcher. It then scans the input + * sequence looking for a match of the pattern. Characters that are not + * part of the match are appended directly to the result string; the match + * is replaced in the result by the applying the replacer function that + * returns a replacement string. The replacement string may contain + * references to captured subsequences as in the {@link #appendReplacement + * appendReplacement} method. + * + *

Note that backslashes (\) and dollar signs ($) in + * the replacement string may cause the results to be different than if it + * were being treated as a literal replacement string. Dollar signs may be + * treated as references to captured subsequences as described above, and + * backslashes are used to escape literal characters in the replacement + * string. + * + *

Given the regular expression dog, the input + * "zzzdogzzzdogzzz", and the function + * mr -> mr.group().toUpperCase(), an invocation of this method on + * a matcher for that expression would yield the string + * "zzzDOGzzzdogzzz". + * + *

Invoking this method changes this matcher's state. If the matcher + * is to be used in further matching operations then it should first be + * reset. + * + *

The replacer function should not modify this matcher's state during + * replacement. This method will, on a best-effort basis, throw a + * {@link java.util.ConcurrentModificationException} if such modification is + * detected. + * + *

The state of the match result passed to the replacer function is + * guaranteed to be constant only for the duration of the replacer function + * call and only if the replacer function does not modify this matcher's + * state. + * + * @implNote + * This implementation applies the replacer function to this matcher, which + * is an instance of {@code MatchResult}. + * + * @param replacer + * The function to be applied to the match result of this matcher + * that returns a replacement string. + * @return The string constructed by replacing the first matching + * subsequence with the result of applying the replacer function to + * the matched subsequence, substituting captured subsequences as + * needed. + * @throws NullPointerException if the replacer function is null + * @throws ConcurrentModificationException if it is detected, on a + * best-effort basis, that the replacer function modified this + * matcher's state + * @since 1.9 + */ + public String replaceFirst(Function replacer) { + Objects.requireNonNull(replacer); + reset(); + if (!find()) + return text.toString(); + StringBuilder sb = new StringBuilder(); + int ec = modCount; + String replacement = replacer.apply(this); + if (ec != modCount) + throw new ConcurrentModificationException(); + appendReplacement(sb, replacement); + appendTail(sb); + return sb.toString(); + } + /** * Sets the limits of this matcher's region. The region is the part of the * input sequence that will be searched to find a match. Invoking this @@ -1365,6 +1698,7 @@ public final class Matcher implements MatchResult { if (!result) this.first = -1; this.oldLast = this.last; + this.modCount++; return result; } @@ -1387,6 +1721,7 @@ public final class Matcher implements MatchResult { if (!result) this.first = -1; this.oldLast = this.last; + this.modCount++; return result; } diff --git a/jdk/test/java/util/regex/PatternStreamTest.java b/jdk/test/java/util/regex/PatternStreamTest.java index 325e07883d8..4ce7bb1b50a 100644 --- a/jdk/test/java/util/regex/PatternStreamTest.java +++ b/jdk/test/java/util/regex/PatternStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2015 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,8 @@ /** * @test - * @bug 8016846 8024341 - * @summary Unit tests for wrapping classes should delegate to default methods + * @bug 8016846 8024341 8071479 + * @summary Unit tests stream and lambda-based methods on Pattern and Matcher * @library ../stream/bootlib * @build java.util.stream.OpTestCase * @run testng/othervm PatternStreamTest @@ -34,158 +34,283 @@ import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.ArrayList; +import java.util.Arrays; +import java.util.ConcurrentModificationException; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.LambdaTestHelpers; import java.util.stream.OpTestCase; import java.util.stream.Stream; import java.util.stream.TestData; +import static org.testng.Assert.*; + @Test public class PatternStreamTest extends OpTestCase { - @DataProvider(name = "Stream") + @DataProvider(name = "Patterns") public static Object[][] makeStreamTestData() { + // Each item must match the type signature of the consumer of this data + // String, String, Pattern List data = new ArrayList<>(); - String description = ""; - String input = "awgqwefg1fefw4vssv1vvv1"; - Pattern pattern = Pattern.compile("4"); - List expected = new ArrayList<>(); - expected.add("awgqwefg1fefw"); - expected.add("vssv1vvv1"); + String description = "All matches"; + String input = "XXXXXX"; + Pattern pattern = Pattern.compile("X"); + data.add(new Object[]{description, input, pattern}); - // Must match the type signature of the consumer of this data, testStrings - // String, String, Pattern, List - data.add(new Object[]{description, input, pattern, expected}); + description = "Bounded every other match"; + input = "XYXYXYYXYX"; + pattern = Pattern.compile("X"); + data.add(new Object[]{description, input, pattern}); + + description = "Every other match"; + input = "YXYXYXYYXYXY"; + pattern = Pattern.compile("X"); + data.add(new Object[]{description, input, pattern}); + + description = ""; + input = "awgqwefg1fefw4vssv1vvv1"; + pattern = Pattern.compile("4"); + data.add(new Object[]{description, input, pattern}); input = "afbfq\u00a3abgwgb\u00a3awngnwggw\u00a3a\u00a3ahjrnhneerh"; pattern = Pattern.compile("\u00a3a"); - expected = new ArrayList<>(); - expected.add("afbfq"); - expected.add("bgwgb"); - expected.add("wngnwggw"); - expected.add(""); - expected.add("hjrnhneerh"); - - data.add(new Object[]{description, input, pattern, expected}); - + data.add(new Object[]{description, input, pattern}); input = "awgqwefg1fefw4vssv1vvv1"; pattern = Pattern.compile("1"); - expected = new ArrayList<>(); - expected.add("awgqwefg"); - expected.add("fefw4vssv"); - expected.add("vvv"); - - data.add(new Object[]{description, input, pattern, expected}); - + data.add(new Object[]{description, input, pattern}); input = "a\u4ebafg1fefw\u4eba4\u9f9cvssv\u9f9c1v\u672c\u672cvv"; pattern = Pattern.compile("1"); - expected = new ArrayList<>(); - expected.add("a\u4ebafg"); - expected.add("fefw\u4eba4\u9f9cvssv\u9f9c"); - expected.add("v\u672c\u672cvv"); - - data.add(new Object[]{description, input, pattern, expected}); - + data.add(new Object[]{description, input, pattern}); input = "1\u56da23\u56da456\u56da7890"; pattern = Pattern.compile("\u56da"); - expected = new ArrayList<>(); - expected.add("1"); - expected.add("23"); - expected.add("456"); - expected.add("7890"); - - data.add(new Object[]{description, input, pattern, expected}); - + data.add(new Object[]{description, input, pattern}); input = "1\u56da23\u9f9c\u672c\u672c\u56da456\u56da\u9f9c\u672c7890"; pattern = Pattern.compile("\u56da"); - expected = new ArrayList<>(); - expected.add("1"); - expected.add("23\u9f9c\u672c\u672c"); - expected.add("456"); - expected.add("\u9f9c\u672c7890"); - - data.add(new Object[]{description, input, pattern, expected}); - + data.add(new Object[]{description, input, pattern}); description = "Empty input"; input = ""; pattern = Pattern.compile("\u56da"); - expected = new ArrayList<>(); - expected.add(""); - - data.add(new Object[]{description, input, pattern, expected}); - + data.add(new Object[]{description, input, pattern}); description = "Empty input with empty pattern"; input = ""; pattern = Pattern.compile(""); - expected = new ArrayList<>(); - expected.add(""); - - data.add(new Object[]{description, input, pattern, expected}); - + data.add(new Object[]{description, input, pattern}); description = "Multiple separators"; input = "This is,testing: with\tdifferent separators."; pattern = Pattern.compile("[ \t,:.]"); - expected = new ArrayList<>(); - expected.add("This"); - expected.add("is"); - expected.add("testing"); - expected.add(""); - expected.add("with"); - expected.add("different"); - expected.add("separators"); - + data.add(new Object[]{description, input, pattern}); description = "Repeated separators within and at end"; input = "boo:and:foo"; pattern = Pattern.compile("o"); - expected = new ArrayList<>(); - expected.add("b"); - expected.add(""); - expected.add(":and:f"); - + data.add(new Object[]{description, input, pattern}); description = "Many repeated separators within and at end"; input = "booooo:and:fooooo"; pattern = Pattern.compile("o"); - expected = new ArrayList<>(); - expected.add("b"); - expected.add(""); - expected.add(""); - expected.add(""); - expected.add(""); - expected.add(":and:f"); + data.add(new Object[]{description, input, pattern}); description = "Many repeated separators before last match"; input = "fooooo:"; pattern = Pattern.compile("o"); - expected = new ArrayList<>(); - expected.add("f"); - expected.add(""); - expected.add(""); - expected.add(""); - expected.add(""); - expected.add(":"); + data.add(new Object[] {description, input, pattern}); - data.add(new Object[] {description, input, pattern, expected}); return data.toArray(new Object[0][]); } - @Test(dataProvider = "Stream") - public void testStrings(String description, String input, Pattern pattern, List expected) { + @Test(dataProvider = "Patterns") + public void testPatternSplitAsStream(String description, String input, Pattern pattern) { + // Derive expected result from pattern.split + List expected = Arrays.asList(pattern.split(input)); + Supplier> ss = () -> pattern.splitAsStream(input); withData(TestData.Factory.ofSupplier(description, ss)) .stream(LambdaTestHelpers.identity()) .expectedResult(expected) .exercise(); } + + @Test(dataProvider = "Patterns") + public void testReplaceFirst(String description, String input, Pattern pattern) { + // Derive expected result from Matcher.replaceFirst(String ) + String expected = pattern.matcher(input).replaceFirst("R"); + String actual = pattern.matcher(input).replaceFirst(r -> "R"); + assertEquals(actual, expected); + } + + @Test(dataProvider = "Patterns") + public void testReplaceAll(String description, String input, Pattern pattern) { + // Derive expected result from Matcher.replaceAll(String ) + String expected = pattern.matcher(input).replaceAll("R"); + String actual = pattern.matcher(input).replaceAll(r -> "R"); + assertEquals(actual, expected); + + // Derive expected result from Matcher.find + Matcher m = pattern.matcher(input); + int expectedMatches = 0; + while (m.find()) { + expectedMatches++; + } + AtomicInteger actualMatches = new AtomicInteger(); + pattern.matcher(input).replaceAll(r -> "R" + actualMatches.incrementAndGet()); + assertEquals(expectedMatches, actualMatches.get()); + } + + @Test(dataProvider = "Patterns") + public void testMatchResults(String description, String input, Pattern pattern) { + // Derive expected result from Matcher.find + Matcher m = pattern.matcher(input); + List expected = new ArrayList<>(); + while (m.find()) { + expected.add(new MatchResultHolder(m)); + } + + Supplier> ss = () -> pattern.matcher(input).results(); + withData(TestData.Factory.ofSupplier(description, ss)) + .stream(s -> s.map(MatchResultHolder::new)) + .expectedResult(expected) + .exercise(); + } + + public void testFailfastMatchResults() { + Pattern p = Pattern.compile("X"); + Matcher m = p.matcher("XX"); + + Stream s = m.results(); + m.find(); + // Should start on the second match + assertEquals(s.count(), 1); + + // Fail fast without short-circuit + // Exercises Iterator.forEachRemaining + m.reset(); + try { + m.results().peek(mr -> m.reset()).count(); + fail(); + } catch (ConcurrentModificationException e) { + // Should reach here + } + + m.reset(); + try { + m.results().peek(mr -> m.find()).count(); + fail(); + } catch (ConcurrentModificationException e) { + // Should reach here + } + + // Fail fast with short-circuit + // Exercises Iterator.hasNext/next + m.reset(); + try { + m.results().peek(mr -> m.reset()).limit(2).count(); + fail(); + } catch (ConcurrentModificationException e) { + // Should reach here + } + + m.reset(); + try { + m.results().peek(mr -> m.find()).limit(2).count(); + fail(); + } catch (ConcurrentModificationException e) { + // Should reach here + } + } + + public void testFailfastReplace() { + Pattern p = Pattern.compile("X"); + Matcher m = p.matcher("XX"); + + // Fail fast without short-circuit + // Exercises Iterator.forEachRemaining + m.reset(); + try { + m.replaceFirst(mr -> { m.reset(); return "Y"; }); + fail(); + } catch (ConcurrentModificationException e) { + // Should reach here + } + + m.reset(); + try { + m.replaceAll(mr -> { m.reset(); return "Y"; }); + fail(); + } catch (ConcurrentModificationException e) { + // Should reach here + } + } + + // A holder of MatchResult that can compare + static class MatchResultHolder implements Comparable { + final MatchResult mr; + + MatchResultHolder(Matcher m) { + this(m.toMatchResult()); + } + + MatchResultHolder(MatchResult mr) { + this.mr = mr; + } + + @Override + public int compareTo(MatchResultHolder that) { + int c = that.mr.group().compareTo(this.mr.group()); + if (c != 0) + return c; + + c = Integer.compare(that.mr.start(), this.mr.start()); + if (c != 0) + return c; + + c = Integer.compare(that.mr.end(), this.mr.end()); + if (c != 0) + return c; + + c = Integer.compare(that.mr.groupCount(), this.mr.groupCount()); + if (c != 0) + return c; + + for (int g = 0; g < this.mr.groupCount(); g++) { + c = that.mr.group(g).compareTo(this.mr.group(g)); + if (c != 0) + return c; + + c = Integer.compare(that.mr.start(g), this.mr.start(g)); + if (c != 0) + return c; + + c = Integer.compare(that.mr.end(g), this.mr.end(g)); + if (c != 0) + return c; + } + return 0; + } + + @Override + public boolean equals(Object that) { + if (this == that) return true; + if (that == null || getClass() != that.getClass()) return false; + + return this.compareTo((MatchResultHolder) that) == 0; + } + + @Override + public int hashCode() { + return mr.group().hashCode(); + } + } } diff --git a/jdk/test/java/util/regex/RegExTest.java b/jdk/test/java/util/regex/RegExTest.java index 3af185aed1f..d5f8123ef5a 100644 --- a/jdk/test/java/util/regex/RegExTest.java +++ b/jdk/test/java/util/regex/RegExTest.java @@ -35,6 +35,7 @@ * 8027645 8035076 8039124 8035975 */ +import java.util.function.Function; import java.util.regex.*; import java.util.Random; import java.io.*; @@ -291,24 +292,26 @@ public class RegExTest { } private static void nullArgumentTest() { - check(new Runnable() { public void run() { Pattern.compile(null); }}); - check(new Runnable() { public void run() { Pattern.matches(null, null); }}); - check(new Runnable() { public void run() { Pattern.matches("xyz", null);}}); - check(new Runnable() { public void run() { Pattern.quote(null);}}); - check(new Runnable() { public void run() { Pattern.compile("xyz").split(null);}}); - check(new Runnable() { public void run() { Pattern.compile("xyz").matcher(null);}}); + check(() -> Pattern.compile(null)); + check(() -> Pattern.matches(null, null)); + check(() -> Pattern.matches("xyz", null)); + check(() -> Pattern.quote(null)); + check(() -> Pattern.compile("xyz").split(null)); + check(() -> Pattern.compile("xyz").matcher(null)); final Matcher m = Pattern.compile("xyz").matcher("xyz"); m.matches(); - check(new Runnable() { public void run() { m.appendTail((StringBuffer)null);}}); - check(new Runnable() { public void run() { m.appendTail((StringBuilder)null);}}); - check(new Runnable() { public void run() { m.replaceAll(null);}}); - check(new Runnable() { public void run() { m.replaceFirst(null);}}); - check(new Runnable() { public void run() { m.appendReplacement((StringBuffer)null, null);}}); - check(new Runnable() { public void run() { m.appendReplacement((StringBuilder)null, null);}}); - check(new Runnable() { public void run() { m.reset(null);}}); - check(new Runnable() { public void run() { Matcher.quoteReplacement(null);}}); - //check(new Runnable() { public void run() { m.usePattern(null);}}); + check(() -> m.appendTail((StringBuffer) null)); + check(() -> m.appendTail((StringBuilder)null)); + check(() -> m.replaceAll((String) null)); + check(() -> m.replaceAll((Function)null)); + check(() -> m.replaceFirst((String)null)); + check(() -> m.replaceFirst((Function) null)); + check(() -> m.appendReplacement((StringBuffer)null, null)); + check(() -> m.appendReplacement((StringBuilder)null, null)); + check(() -> m.reset(null)); + check(() -> Matcher.quoteReplacement(null)); + //check(() -> m.usePattern(null)); report("Null Argument"); }