From 21d977eaf591e527d0cd5f4053da231f8f7b8f8a Mon Sep 17 00:00:00 2001 From: Appu Goundan Date: Wed, 5 Jun 2024 14:05:51 -0400 Subject: [PATCH] Add StringMatcher interface Include two implementations regex and string. Signed-off-by: Appu Goundan --- .../strings/RegexSyntaxException.java | 23 ++++++ .../dev/sigstore/strings/StringMatcher.java | 78 +++++++++++++++++++ .../sigstore/strings/StringMatcherTest.java | 50 ++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 sigstore-java/src/main/java/dev/sigstore/strings/RegexSyntaxException.java create mode 100644 sigstore-java/src/main/java/dev/sigstore/strings/StringMatcher.java create mode 100644 sigstore-java/src/test/java/dev/sigstore/strings/StringMatcherTest.java diff --git a/sigstore-java/src/main/java/dev/sigstore/strings/RegexSyntaxException.java b/sigstore-java/src/main/java/dev/sigstore/strings/RegexSyntaxException.java new file mode 100644 index 00000000..46598b10 --- /dev/null +++ b/sigstore-java/src/main/java/dev/sigstore/strings/RegexSyntaxException.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024 The Sigstore Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.sigstore.strings; + +/** Check exception wrapper around {@link java.util.regex.PatternSyntaxException}. */ +public class RegexSyntaxException extends Exception { + public RegexSyntaxException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/sigstore-java/src/main/java/dev/sigstore/strings/StringMatcher.java b/sigstore-java/src/main/java/dev/sigstore/strings/StringMatcher.java new file mode 100644 index 00000000..49d462a4 --- /dev/null +++ b/sigstore-java/src/main/java/dev/sigstore/strings/StringMatcher.java @@ -0,0 +1,78 @@ +/* + * Copyright 2024 The Sigstore Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.sigstore.strings; + +import java.util.Objects; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * An interface for allowing direct string matching or regular expressions. Use the static factory + * {@link #string(String)} or {@link #regex(String)} to instantiate the appropriate matcher. Custom + * implementations should override {@link Object#toString} for better error reporting. + */ +public interface StringMatcher extends Predicate { + + /** Create a matcher for string equality. */ + static StringMatcher string(String string) { + Objects.requireNonNull(string, "string matcher cannot be initialized with null string"); + return new StringMatcher() { + @Override + public boolean test(String target) { + return string.equals(target); + } + + @Override + public String toString() { + return "'String: " + string + "'"; + } + }; + } + + /** + * Create a matcher using regular expressions. Regex matching ignores null values and returns + * false instead of erroring. + * + * @param string the input pattern + * @return a regex based instance + * @throws RegexSyntaxException if the input pattern is not valid regex. This is a runtime + * exception and probably should be handled + */ + static StringMatcher regex(String string) throws RegexSyntaxException { + Objects.requireNonNull(string, "string matcher cannot be initialized with null regex"); + Pattern pattern; + try { + pattern = Pattern.compile(string); + } catch (PatternSyntaxException ex) { + throw new RegexSyntaxException("Could not parse regex: '" + string + "'", ex); + } + return new StringMatcher() { + @Override + public boolean test(String target) { + if (target == null) { + return false; + } + return pattern.matcher(target).matches(); + } + + @Override + public String toString() { + return "'RegEx: " + pattern + "'"; + } + }; + } +} diff --git a/sigstore-java/src/test/java/dev/sigstore/strings/StringMatcherTest.java b/sigstore-java/src/test/java/dev/sigstore/strings/StringMatcherTest.java new file mode 100644 index 00000000..e3b1f56b --- /dev/null +++ b/sigstore-java/src/test/java/dev/sigstore/strings/StringMatcherTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2024 The Sigstore Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dev.sigstore.strings; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class StringMatcherTest { + + @Test + public void testString() { + var testMatcher = StringMatcher.string("testtest"); + Assertions.assertEquals("'String: testtest'", testMatcher.toString()); + + Assertions.assertTrue(testMatcher.test("testtest")); + Assertions.assertFalse(testMatcher.test("testtest1")); + Assertions.assertFalse(testMatcher.test("")); + Assertions.assertFalse(testMatcher.test(null)); + } + + @Test + public void testRegex() throws Exception { + var testMatcher = StringMatcher.regex("abc...xyz"); + Assertions.assertEquals("'RegEx: abc...xyz'", testMatcher.toString()); + + Assertions.assertTrue(testMatcher.test("abc888xyz")); + Assertions.assertFalse(testMatcher.test("abc888xyzEXTRA")); + Assertions.assertFalse(testMatcher.test("abcxyz")); + Assertions.assertFalse(testMatcher.test("")); + Assertions.assertFalse(testMatcher.test(null)); + } + + @Test + public void testRegex_initFailure() { + Assertions.assertThrows(RegexSyntaxException.class, () -> StringMatcher.regex("asdf\\")); + } +}