diff --git a/src/main/java/io/skelp/verifier/type/StringVerifier.java b/src/main/java/io/skelp/verifier/type/StringVerifier.java index 3dc7e20..86b1241 100644 --- a/src/main/java/io/skelp/verifier/type/StringVerifier.java +++ b/src/main/java/io/skelp/verifier/type/StringVerifier.java @@ -814,6 +814,136 @@ public StringVerifier match(final Pattern pattern) { return this; } + /** + *

+ * Verifies that the value matches all of the regular expressions provided. + *

+ *

+ * {@literal null} references are handled gracefully without exceptions. + *

+ *
+     * Verifier.verify(*).matchAll()                      => PASS
+     * Verifier.verify(*).matchAll((CharSequence[]) null) => PASS
+     * Verifier.verify(*).matchAll(*, null)               => FAIL
+     * Verifier.verify((String) null).matchAll(*)         => FAIL
+     * Verifier.verify("foo").matchAll(".*", "fo{2}")     => PASS
+     * Verifier.verify("foo").matchAll(".*", "fiz{2}")    => FAIL
+     * 
+ * + * @param regexes + * the regular expressions to be matched against of the value (may be {@literal null} or contain {@literal + * null} references) + * @return A reference to this {@link StringVerifier} for chaining purposes. + * @throws VerifierException + * If the verification fails while not negated or passes while negated. + * @see #matchAll(Pattern...) + */ + public StringVerifier matchAll(final CharSequence... regexes) { + final String value = verification().getValue(); + final boolean result = value != null && matchAll(regexes, input -> input != null && value.matches(input.toString())); + + verification().report(result, MessageKeys.MATCH_ALL, (Object) regexes); + + return this; + } + + /** + *

+ * Verifies that the value matches all of the regular expression {@code patterns} provided. + *

+ *

+ * {@literal null} references are handled gracefully without exceptions. + *

+ *
+     * Verifier.verify(*).matchAll()                                                     => PASS
+     * Verifier.verify(*).matchAll((Pattern[]) null)                                     => PASS
+     * Verifier.verify(*).matchAll(*, null)                                              => FAIL
+     * Verifier.verify((String) null).matchAll(*)                                        => FAIL
+     * Verifier.verify("foo").matchAll(Pattern.compile(".*"), Pattern.compile("fo{2}"))  => PASS
+     * Verifier.verify("foo").matchAll(Pattern.compile(".*"), Pattern.compile("fiz{2}")) => FAIL
+     * 
+ * + * @param patterns + * the regular expression {@code Patterns} to be matched against of the value (may be {@literal null} or + * contain {@literal null} references) + * @return A reference to this {@link StringVerifier} for chaining purposes. + * @throws VerifierException + * If the verification fails while not negated or passes while negated. + * @see #matchAll(CharSequence...) + */ + public StringVerifier matchAll(final Pattern... patterns) { + final String value = verification().getValue(); + final boolean result = value != null && matchAll(patterns, input -> input != null && input.matcher(value).matches()); + + verification().report(result, MessageKeys.MATCH_ALL, (Object) patterns); + + return this; + } + + /** + *

+ * Verifies that the value matches any of the regular expressions provided. + *

+ *

+ * {@literal null} references are handled gracefully without exceptions. + *

+ *
+     * Verifier.verify(*).matchAny()                       => FAIL
+     * Verifier.verify(*).matchAny((CharSequence[]) null)  => FAIL
+     * Verifier.verify((String) null).matchAny(*)          => FAIL
+     * Verifier.verify("foo").matchAny("fo{2}", "fiz{2}")  => PASS
+     * Verifier.verify("foo").matchAny("fiz{2}", "buz{2}") => FAIL
+     * 
+ * + * @param regexes + * the regular expressions to be matched against of the value (may be {@literal null} or contain {@literal + * null} references) + * @return A reference to this {@link StringVerifier} for chaining purposes. + * @throws VerifierException + * If the verification fails while not negated or passes while negated. + * @see #matchAny(Pattern...) + */ + public StringVerifier matchAny(final CharSequence... regexes) { + final String value = verification().getValue(); + final boolean result = value != null && matchAny(regexes, input -> input != null && value.matches(input.toString())); + + verification().report(result, MessageKeys.MATCH_ANY, (Object) regexes); + + return this; + } + + /** + *

+ * Verifies that the value matches any of the regular expression {@code patterns} provided. + *

+ *

+ * {@literal null} references are handled gracefully without exceptions. + *

+ *
+     * Verifier.verify(*).matchAny()                                                         => FAIL
+     * Verifier.verify(*).matchAny((Pattern[]) null)                                         => FAIL
+     * Verifier.verify((String) null).matchAny(*)                                            => FAIL
+     * Verifier.verify("foo").matchAny(Pattern.compile("fo{2}"), Pattern.compile("fiz{2}"))  => PASS
+     * Verifier.verify("foo").matchAny(Pattern.compile("fiz{2}"), Pattern.compile("buz{2}")) => FAIL
+     * 
+ * + * @param patterns + * the regular expression {@code Patterns} to be matched against of the value (may be {@literal null} or + * contain {@literal null} references) + * @return A reference to this {@link StringVerifier} for chaining purposes. + * @throws VerifierException + * If the verification fails while not negated or passes while negated. + * @see #matchAny(CharSequence...) + */ + public StringVerifier matchAny(final Pattern... patterns) { + final String value = verification().getValue(); + final boolean result = value != null && matchAny(patterns, input -> input != null && input.matcher(value).matches()); + + verification().report(result, MessageKeys.MATCH_ANY, (Object) patterns); + + return this; + } + /** *

* Verifies that the value contains only digits. @@ -1096,6 +1226,8 @@ enum MessageKeys implements MessageKey { EQUAL_TO_IGNORE_CASE("io.skelp.verifier.type.StringVerifier.equalToIgnoreCase"), LOWER_CASE("io.skelp.verifier.type.StringVerifier.lowerCase"), MATCH("io.skelp.verifier.type.StringVerifier.match"), + MATCH_ALL("io.skelp.verifier.type.StringVerifier.matchAll"), + MATCH_ANY("io.skelp.verifier.type.StringVerifier.matchAny"), NUMERIC("io.skelp.verifier.type.StringVerifier.numeric"), NUMERIC_SPACE("io.skelp.verifier.type.StringVerifier.numericSpace"), SIZE_OF("io.skelp.verifier.type.StringVerifier.sizeOf"), diff --git a/src/main/resources/Verifier.properties b/src/main/resources/Verifier.properties index e1250c7..03602ee 100644 --- a/src/main/resources/Verifier.properties +++ b/src/main/resources/Verifier.properties @@ -133,6 +133,8 @@ io.skelp.verifier.type.StringVerifier.equalToAnyIgnoreCase=be equal to any {0} ( io.skelp.verifier.type.StringVerifier.equalToIgnoreCase=be equal to ''{0}'' (ignore case) io.skelp.verifier.type.StringVerifier.lowerCase=be all lower case io.skelp.verifier.type.StringVerifier.match=match ''{0}'' +io.skelp.verifier.type.StringVerifier.matchAll=match all {0} +io.skelp.verifier.type.StringVerifier.matchAny=match any {0} io.skelp.verifier.type.StringVerifier.numeric=contain only digits io.skelp.verifier.type.StringVerifier.numericSpace=contain only digits or space io.skelp.verifier.type.StringVerifier.sizeOf=have a size of ''{0,number,integer}'' diff --git a/src/test/java/io/skelp/verifier/type/StringVerifierTest.java b/src/test/java/io/skelp/verifier/type/StringVerifierTest.java index 423d242..0592059 100644 --- a/src/test/java/io/skelp/verifier/type/StringVerifierTest.java +++ b/src/test/java/io/skelp/verifier/type/StringVerifierTest.java @@ -1501,7 +1501,7 @@ public void testMatchWhenValueIsEmptyAndNotMatch() { } @Test - public void testMatchWhenValueIsEmptyAndNotMatchAndOtherIsCharSequence() { + public void testMatchWhenValueIsEmptyAndNotMatchAndRegexIsCharSequence() { testMatchHelper(EMPTY, new StringWrapper("fo{2}"), false); } @@ -1511,7 +1511,7 @@ public void testMatchWhenValueIsMatch() { } @Test - public void testMatchWhenValueIsMatchAndOtherIsCharSequence() { + public void testMatchWhenValueIsMatchAndRegexIsCharSequence() { testMatchHelper("foo", new StringWrapper("fo{2}"), true); } @@ -1535,6 +1535,122 @@ private void testMatchHelper(String value, CharSequence regex, boolean expected) assertSame("Passes regex for message formatting", regex, getArgsCaptor().getValue()); } + @Test + public void testMatchAllWhenNoRegularExpressions() { + testMatchAllHelper("foo", createEmptyArray(CharSequence.class), true); + } + + @Test + public void testMatchAllWhenRegularExpressionIsNull() { + testMatchAllHelper("foo", createArray((CharSequence) null), false); + } + + @Test + public void testMatchAllWhenRegularExpressionsIsNull() { + testMatchAllHelper("foo", null, true); + } + + @Test + public void testMatchAllWhenValueMatchesAllRegularExpressions() { + testMatchAllHelper("foo", createArray("fo{2}", ".*"), true); + } + + @Test + public void testMatchAllWhenValueMatchesAllRegularExpressionsWhenNotCharSequences() { + testMatchAllHelper("foo", createArray(new StringWrapper("fo{2}"), new StringWrapper(".*")), true); + } + + @Test + public void testMatchAllWhenValueMatchesSomeRegularExpressions() { + testMatchAllHelper("foo", createArray("fo{2}", "fiz{2}"), false); + } + + @Test + public void testMatchAllWhenValueMatchesSomeRegularExpressionsWhenNotCharSequences() { + testMatchAllHelper("foo", createArray(new StringWrapper("fo{2}"), new StringWrapper("fiz{2}")), false); + } + + @Test + public void testMatchAllWhenValueDoesNotMatchAnyRegularExpression() { + testMatchAllHelper("foo", createArray("fiz{2}", "buz{2}"), false); + } + + @Test + public void testMatchAllWhenValueDoesNotMatchAnyRegularExpressionWhenNotCharSequences() { + testMatchAllHelper("foo", createArray(new StringWrapper("fiz{2}"), new StringWrapper("buz{2}")), false); + } + + @Test + public void testMatchAllWhenValueIsNull() { + testMatchAllHelper(null, createArray(".*"), false); + } + + private void testMatchAllHelper(String value, CharSequence[] regexes, boolean expected) { + setValue(value); + + assertSame("Chains reference", getCustomVerifier(), getCustomVerifier().matchAll(regexes)); + + verify(getMockVerification()).report(expected, StringVerifier.MessageKeys.MATCH_ALL, (Object) regexes); + } + + @Test + public void testMatchAnyWhenNoRegularExpressions() { + testMatchAnyHelper("foo", createEmptyArray(CharSequence.class), false); + } + + @Test + public void testMatchAnyWhenRegularExpressionIsNull() { + testMatchAnyHelper("foo", createArray((CharSequence) null), false); + } + + @Test + public void testMatchAnyWhenRegularExpressionsIsNull() { + testMatchAnyHelper("foo", null, false); + } + + @Test + public void testMatchAnyWhenValueMatchesAllRegularExpressions() { + testMatchAnyHelper("foo", createArray("fo{2}", ".*"), true); + } + + @Test + public void testMatchAnyWhenValueMatchesAllRegularExpressionsWhenNotCharSequences() { + testMatchAnyHelper("foo", createArray(new StringWrapper("fo{2}"), new StringWrapper(".*")), true); + } + + @Test + public void testMatchAnyWhenValueMatchesSomeRegularExpressions() { + testMatchAnyHelper("foo", createArray("fo{2}", "fiz{2}"), true); + } + + @Test + public void testMatchAnyWhenValueMatchesSomeRegularExpressionsWhenNotCharSequences() { + testMatchAnyHelper("foo", createArray(new StringWrapper("fo{2}"), new StringWrapper("fiz{2}")), true); + } + + @Test + public void testMatchAnyWhenValueDoesNotMatchAnyRegularExpression() { + testMatchAnyHelper("foo", createArray("fiz{2}", "buz{2}"), false); + } + + @Test + public void testMatchAnyWhenValueDoesNotMatchAnyRegularExpressionWhenNotCharSequences() { + testMatchAnyHelper("foo", createArray(new StringWrapper("fiz{2}"), new StringWrapper("buz{2}")), false); + } + + @Test + public void testMatchAnyWhenValueIsNull() { + testMatchAnyHelper(null, createArray(".*"), false); + } + + private void testMatchAnyHelper(String value, CharSequence[] regexes, boolean expected) { + setValue(value); + + assertSame("Chains reference", getCustomVerifier(), getCustomVerifier().matchAny(regexes)); + + verify(getMockVerification()).report(expected, StringVerifier.MessageKeys.MATCH_ANY, (Object) regexes); + } + @Test public void testMatchWithPatternWhenPatternIsNull() { testMatchWithPatternHelper("foo", null, false); @@ -1575,6 +1691,92 @@ private void testMatchWithPatternHelper(String value, Pattern pattern, boolean e assertSame("Passes pattern for message formatting", pattern, getArgsCaptor().getValue()); } + @Test + public void testMatchAllWithPatternsWhenNoPatterns() { + testMatchAllWithPatternsHelper("foo", createEmptyArray(Pattern.class), true); + } + + @Test + public void testMatchAllWithPatternsWhenPatternIsNull() { + testMatchAllWithPatternsHelper("foo", createArray((Pattern) null), false); + } + + @Test + public void testMatchAllWithPatternsWhenPatternsIsNull() { + testMatchAllWithPatternsHelper("foo", null, true); + } + + @Test + public void testMatchAllWithPatternsWhenValueMatchesAllPatterns() { + testMatchAllWithPatternsHelper("foo", createArray(Pattern.compile("fo{2}"), Pattern.compile(".*")), true); + } + + @Test + public void testMatchAllWithPatternsWhenValueMatchesSomePatterns() { + testMatchAllWithPatternsHelper("foo", createArray(Pattern.compile("fo{2}"), Pattern.compile("fiz{2}")), false); + } + + @Test + public void testMatchAllWithPatternsWhenValueDoesNotMatchAnyPattern() { + testMatchAllWithPatternsHelper("foo", createArray(Pattern.compile("fiz{2}"), Pattern.compile("buz{2}")), false); + } + + @Test + public void testMatchAllWithPatternsWhenValueIsNull() { + testMatchAllWithPatternsHelper(null, createArray(Pattern.compile(".*")), false); + } + + private void testMatchAllWithPatternsHelper(String value, Pattern[] patterns, boolean expected) { + setValue(value); + + assertSame("Chains reference", getCustomVerifier(), getCustomVerifier().matchAll(patterns)); + + verify(getMockVerification()).report(expected, StringVerifier.MessageKeys.MATCH_ALL, (Object) patterns); + } + + @Test + public void testMatchAnyWithPatternsWhenNoPatterns() { + testMatchAnyWithPatternsHelper("foo", createEmptyArray(Pattern.class), false); + } + + @Test + public void testMatchAnyWithPatternsWhenPatternIsNull() { + testMatchAnyWithPatternsHelper("foo", createArray((Pattern) null), false); + } + + @Test + public void testMatchAnyWithPatternsWhenPatternsIsNull() { + testMatchAnyWithPatternsHelper("foo", null, false); + } + + @Test + public void testMatchAnyWithPatternsWhenValueMatchesAllPatterns() { + testMatchAnyWithPatternsHelper("foo", createArray(Pattern.compile("fo{2}"), Pattern.compile(".*")), true); + } + + @Test + public void testMatchAnyWithPatternsWhenValueMatchesSomePatterns() { + testMatchAnyWithPatternsHelper("foo", createArray(Pattern.compile("fo{2}"), Pattern.compile("fiz{2}")), true); + } + + @Test + public void testMatchAnyWithPatternsWhenValueDoesNotMatchAnyPattern() { + testMatchAnyWithPatternsHelper("foo", createArray(Pattern.compile("fiz{2}"), Pattern.compile("buz{2}")), false); + } + + @Test + public void testMatchAnyWithPatternsWhenValueIsNull() { + testMatchAnyWithPatternsHelper(null, createArray(Pattern.compile(".*")), false); + } + + private void testMatchAnyWithPatternsHelper(String value, Pattern[] patterns, boolean expected) { + setValue(value); + + assertSame("Chains reference", getCustomVerifier(), getCustomVerifier().matchAny(patterns)); + + verify(getMockVerification()).report(expected, StringVerifier.MessageKeys.MATCH_ANY, (Object) patterns); + } + @Test public void testNumericWhenValueIsEmpty() { testNumericHelper(EMPTY, true); @@ -2112,6 +2314,8 @@ protected Map getMessageKeys() { messageKeys.put("EQUAL_TO_IGNORE_CASE", "io.skelp.verifier.type.StringVerifier.equalToIgnoreCase"); messageKeys.put("LOWER_CASE", "io.skelp.verifier.type.StringVerifier.lowerCase"); messageKeys.put("MATCH", "io.skelp.verifier.type.StringVerifier.match"); + messageKeys.put("MATCH_ALL", "io.skelp.verifier.type.StringVerifier.matchAll"); + messageKeys.put("MATCH_ANY", "io.skelp.verifier.type.StringVerifier.matchAny"); messageKeys.put("NUMERIC", "io.skelp.verifier.type.StringVerifier.numeric"); messageKeys.put("NUMERIC_SPACE", "io.skelp.verifier.type.StringVerifier.numericSpace"); messageKeys.put("SIZE_OF", "io.skelp.verifier.type.StringVerifier.sizeOf");