mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-14 16:32:06 +01:00
Add support for different regex parsers
This commit is contained in:
parent
3f10efac7a
commit
21f8ba8ccf
8 changed files with 270 additions and 1 deletions
28
hsregex/build.gradle
Normal file
28
hsregex/build.gradle
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java-library'
|
||||||
|
}
|
||||||
|
|
||||||
|
group 'org.pgpainless'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||||
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||||
|
|
||||||
|
implementation(project(":pgpainless-core"))
|
||||||
|
|
||||||
|
// Henry Spencers Regular Expression (RegEx packets)
|
||||||
|
implementation 'com.basistech.tclre:tcl-regex:0.14.5'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.algorithm;
|
||||||
|
|
||||||
|
import com.basistech.tclre.HsrePattern;
|
||||||
|
import com.basistech.tclre.PatternFlags;
|
||||||
|
import com.basistech.tclre.RePattern;
|
||||||
|
import com.basistech.tclre.RegexException;
|
||||||
|
|
||||||
|
public class HSRegexInterpreterFactory extends RegexInterpreterFactory {
|
||||||
|
|
||||||
|
public Regex instantiate(String regex) {
|
||||||
|
return new Regex() {
|
||||||
|
|
||||||
|
private final RePattern pattern;
|
||||||
|
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
pattern = HsrePattern.compile(regex, PatternFlags.ADVANCED);
|
||||||
|
} catch (RegexException e) {
|
||||||
|
throw new IllegalArgumentException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(String string) {
|
||||||
|
return pattern.matcher(string).find();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regex interpreter implementation based on Henry Spencers Regular Expression library.
|
||||||
|
*
|
||||||
|
* @see <a href="https://www.rfc-editor.org/rfc/rfc4880#section-8">RFC4880 - §8. Regular Expressions</a>
|
||||||
|
*/
|
||||||
|
package org.pgpainless.algorithm;
|
|
@ -30,4 +30,7 @@ dependencies {
|
||||||
|
|
||||||
// @Nullable, @Nonnull annotations
|
// @Nullable, @Nonnull annotations
|
||||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||||
|
|
||||||
|
// HSRE regex for tests
|
||||||
|
testImplementation project(":hsregex")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.algorithm;
|
||||||
|
|
||||||
|
import org.pgpainless.key.util.UserId;
|
||||||
|
|
||||||
|
public interface Regex {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true, if the regex matches the given user-id.
|
||||||
|
*
|
||||||
|
* @param userId userId
|
||||||
|
* @return true if matches, false otherwise
|
||||||
|
*/
|
||||||
|
default boolean matches(UserId userId) {
|
||||||
|
return matches(userId.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true, if the regex matches the given string.
|
||||||
|
*
|
||||||
|
* @param string string
|
||||||
|
* @return true if matches, false otherwise
|
||||||
|
*/
|
||||||
|
boolean matches(String string);
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.algorithm;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public abstract class RegexInterpreterFactory {
|
||||||
|
|
||||||
|
private static RegexInterpreterFactory INSTANCE;
|
||||||
|
|
||||||
|
public static RegexInterpreterFactory getInstance() {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
INSTANCE = new JavaRegexInterpreterFactory();
|
||||||
|
}
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setInstance(@Nonnull RegexInterpreterFactory instance) {
|
||||||
|
INSTANCE = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Regex create(String regex) {
|
||||||
|
return getInstance().instantiate(regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Regex instantiate(String regex) throws IllegalArgumentException;
|
||||||
|
|
||||||
|
public static class JavaRegexInterpreterFactory extends RegexInterpreterFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Regex instantiate(String regex) {
|
||||||
|
return new Regex() {
|
||||||
|
|
||||||
|
private final Pattern pattern = Pattern.compile(regex);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(String string) {
|
||||||
|
return pattern.matcher(string).find();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.algorithm;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Named;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.pgpainless.key.util.UserId;
|
||||||
|
|
||||||
|
public class RegexTest {
|
||||||
|
private static Stream<Arguments> provideRegexInterpreterFactories() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(Named.of("Default JavaRegexInterpreterFactory",
|
||||||
|
new RegexInterpreterFactory.JavaRegexInterpreterFactory())),
|
||||||
|
Arguments.of(Named.of("HSRegexInterpreterFactory",
|
||||||
|
new HSRegexInterpreterFactory()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideRegexInterpreterFactories")
|
||||||
|
public void simpleTest(RegexInterpreterFactory factory) {
|
||||||
|
Regex regex = factory.instantiate("Alice|Bob");
|
||||||
|
assertTrue(regex.matches("Alice"));
|
||||||
|
assertTrue(regex.matches("Bob"));
|
||||||
|
assertFalse(regex.matches("Charlie"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideRegexInterpreterFactories")
|
||||||
|
public void testEmailRegexMatchesDomain(RegexInterpreterFactory factory) {
|
||||||
|
Regex regex = factory.instantiate("<[^>]+[@.]pgpainless\\.org>$");
|
||||||
|
assertTrue(regex.matches("Alice <alice@pgpainless.org>"));
|
||||||
|
assertTrue(regex.matches("Bob <bob@pgpainless.org>"));
|
||||||
|
assertFalse(regex.matches("Alice <alice@example.com>"), "wrong domain");
|
||||||
|
assertFalse(regex.matches("Bob <bob@example.com>"), "wrong domain");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideRegexInterpreterFactories")
|
||||||
|
public void testEmailRegexMatchesOnlyWrappedAddresses(RegexInterpreterFactory factory) {
|
||||||
|
Regex regex = factory.instantiate("<[^>]+[@.]pgpainless\\.org>$");
|
||||||
|
assertTrue(regex.matches("<alice@pgpainless.org>"));
|
||||||
|
assertFalse(regex.matches("alice@pgpainless.org"), "only match mails in <>");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideRegexInterpreterFactories")
|
||||||
|
public void testCaseSensitivity(RegexInterpreterFactory factory) {
|
||||||
|
Regex regex = factory.instantiate("<[^>]+[@.]pgpainless\\.org>$");
|
||||||
|
assertFalse(regex.matches("Alice <alice@PGPAINLESS.ORG>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideRegexInterpreterFactories")
|
||||||
|
public void testWildCard(RegexInterpreterFactory factory) {
|
||||||
|
Regex regex = factory.instantiate(".*");
|
||||||
|
assertTrue(regex.matches(""));
|
||||||
|
assertTrue(regex.matches("Alice"));
|
||||||
|
assertTrue(regex.matches("<alice@pgpainless.org>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideRegexInterpreterFactories")
|
||||||
|
public void testExclusion(RegexInterpreterFactory factory) {
|
||||||
|
// Test [^>] matches all but '>'
|
||||||
|
Regex regex = factory.instantiate("<[^>]+[@.]pgpainless\\.org>$");
|
||||||
|
assertFalse(regex.matches("<alice>appendix@pgpainless.org>"));
|
||||||
|
assertFalse(regex.matches("<>alice@pgpainless.org>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideRegexInterpreterFactories")
|
||||||
|
public void testOnlyMatchAtTheEnd(RegexInterpreterFactory factory) {
|
||||||
|
Regex regex = factory.instantiate("<[^>]+[@.]pgpainless\\.org>$");
|
||||||
|
assertFalse(regex.matches("Alice <alice@pgpainless.org><bob@example.org>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideRegexInterpreterFactories")
|
||||||
|
public void testRanges(RegexInterpreterFactory factory) {
|
||||||
|
Regex regex = factory.instantiate("<[^>]+[0-9][@.]pgpainless\\.org>$");
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
String mail = "<user" + i + "@pgpainless.org>";
|
||||||
|
assertTrue(regex.matches(mail));
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFalse(regex.matches("<user@pgpainless.org>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideRegexInterpreterFactories")
|
||||||
|
public void testExactMailMatch(RegexInterpreterFactory factory) {
|
||||||
|
Regex exactMail = factory.instantiate("<exact@pgpainless\\.org>$");
|
||||||
|
assertTrue(exactMail.matches("<exact@pgpainless.org>"));
|
||||||
|
assertTrue(exactMail.matches("Exact Match <exact@pgpainless.org>"));
|
||||||
|
assertFalse(exactMail.matches("<roughly-exact@pgpainless.org>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("provideRegexInterpreterFactories")
|
||||||
|
public void testSetInstance(RegexInterpreterFactory factory) {
|
||||||
|
RegexInterpreterFactory before = RegexInterpreterFactory.getInstance();
|
||||||
|
RegexInterpreterFactory.setInstance(factory);
|
||||||
|
|
||||||
|
Regex regex = RegexInterpreterFactory.create("<[^>]+[@.]pgpainless\\.org>$");
|
||||||
|
assertTrue(regex.matches(UserId.nameAndEmail("Alice", "alice@pgpainless.org")));
|
||||||
|
|
||||||
|
RegexInterpreterFactory.setInstance(before);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,5 +6,6 @@ rootProject.name = 'PGPainless'
|
||||||
|
|
||||||
include 'pgpainless-core',
|
include 'pgpainless-core',
|
||||||
'pgpainless-sop',
|
'pgpainless-sop',
|
||||||
'pgpainless-cli'
|
'pgpainless-cli',
|
||||||
|
'hsregex'
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue