Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>org.rcsb</groupId>
<artifactId>rcsb-common</artifactId>
<version>3.1.1-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>rcsb-common</name>
<description>Common constants for RCSB projects.</description>
Expand Down
76 changes: 71 additions & 5 deletions src/main/java/org/rcsb/common/constants/IdentifierRegex.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,82 @@

import java.util.regex.Pattern;

/**
* Regular expressions for PDB identifiers, plus convenience methods to test strings against them.
*
* <p>Two identifier flavours are supported: the legacy 4-character id (e.g. {@code 1abc}) and the
* extended id (e.g. {@code pdb_00001abc}). Both may optionally carry a suffix built from the
* separators in {@link IdentifierSeparator}.
*/
public class IdentifierRegex {

public static final Pattern PDB_ID_REGEX = Pattern.compile("^\\d\\w\\w\\w$");
private static final String ALPHANUM_VALUE = "[a-zA-Z0-9]";

private static final String PDB_ID_WITH_SUFFIX_REGEX_VALUE = String.format(
"^\\d\\w\\w\\w(?:%s\\w+(?:%s\\w+)?|%s\\w+|%s\\w+)?$",
private static final String PDB_ID_REGEX_VALUE = "\\d" + ALPHANUM_VALUE + "{3}";

private static final String EXT_PDB_ID_REGEX_VALUE = "pdb_[a-z0-9]{8}";

private static final String SUFFIX_REGEX_VALUE = String.format(
"(?:%1$s%5$s+(?:%2$s%5$s+)?|%3$s%5$s+|%4$s%5$s+)?",
Pattern.quote(IdentifierSeparator.ASSEMBLY_SEPARATOR),
Pattern.quote(IdentifierSeparator.INTERFACE_SEPARATOR),
Pattern.quote(IdentifierSeparator.ENTITY_SEPARATOR),
Pattern.quote(IdentifierSeparator.ENTITY_INSTANCE_SEPARATOR));
Pattern.quote(IdentifierSeparator.ENTITY_INSTANCE_SEPARATOR),
ALPHANUM_VALUE);

/** A legacy PDB id: a digit followed by 3 alphanumeric characters, e.g. {@code 1abc}. */
public static final Pattern PDB_ID_REGEX = Pattern.compile("^" + PDB_ID_REGEX_VALUE + "$");

/** An extended PDB id: {@code pdb_} followed by 8 lowercase alphanumeric characters, e.g. {@code pdb_00001abc}. */
public static final Pattern EXT_PDB_ID_REGEX = Pattern.compile("^" + EXT_PDB_ID_REGEX_VALUE + "$");

/** A legacy PDB id with an optional assembly, interface, entity or entity instance suffix. */
public static final Pattern PDB_ID_WITH_SUFFIX_REGEX =
Pattern.compile("^" + PDB_ID_REGEX_VALUE + SUFFIX_REGEX_VALUE + "$");

/** An extended PDB id with an optional assembly, interface, entity or entity instance suffix. */
public static final Pattern EXT_PDB_ID_WITH_SUFFIX_REGEX =
Pattern.compile("^" + EXT_PDB_ID_REGEX_VALUE + SUFFIX_REGEX_VALUE + "$");

private IdentifierRegex() {}

/**
* Tells whether the given string is a legacy PDB id: a digit followed by 3 alphanumeric
* characters, in either case (e.g. {@code 1abc}, {@code 1ABC}).
*
* <p>Suffixes are not accepted: use {@link #PDB_ID_WITH_SUFFIX_REGEX} for those.
*
* @param id the string to test, can be null
* @return true if the string is a legacy PDB id, false otherwise or if id is null
* @since 3.2.0
*/
public static boolean isLegacyPdbId(String id) {
return id != null && PDB_ID_REGEX.matcher(id).matches();
}

/**
* Tells whether the given string is an extended PDB id: the prefix {@code pdb_} followed by 8
* lowercase alphanumeric characters (e.g. {@code pdb_00001abc}). Uppercase is not accepted.
*
* <p>Suffixes are not accepted: use {@link #EXT_PDB_ID_WITH_SUFFIX_REGEX} for those.
*
* @param id the string to test, can be null
* @return true if the string is an extended PDB id, false otherwise or if id is null
* @since 3.2.0
*/
public static boolean isExtPdbId(String id) {
return id != null && EXT_PDB_ID_REGEX.matcher(id).matches();
}

public static final Pattern PDB_ID_WITH_SUFFIX_REGEX = Pattern.compile(PDB_ID_WITH_SUFFIX_REGEX_VALUE);
/**
* Tells whether the given string is a PDB id in either of the supported flavours, i.e. whether
* {@link #isLegacyPdbId(String)} or {@link #isExtPdbId(String)} holds.
*
* @param id the string to test, can be null
* @return true if the string is a legacy or extended PDB id, false otherwise or if id is null
* @since 3.2.0
*/
public static boolean isAnyPdbId(String id) {
return isLegacyPdbId(id) || isExtPdbId(id);
}
}
130 changes: 126 additions & 4 deletions src/test/java/org/rcsb/common/constants/IdentifierRegexTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,55 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.stream.Stream;

class IdentifierRegexTest {

@ParameterizedTest(name = "matches PDB ID: {0}")
@MethodSource("validPdbIds")
void matchesPdbId(String pdbId) {
assertTrue(IdentifierRegex.PDB_ID_REGEX.matcher(pdbId).matches());
}

private static Stream<String> validPdbIds() {
return Stream.of("1abc", "1ABC", "1AbC", "1234");
}

@ParameterizedTest(name = "does not match PDB ID: {0}")
@MethodSource("invalidPdbIds")
void doesNotMatchInvalidPdbId(String pdbId) {
assertFalse(IdentifierRegex.PDB_ID_REGEX.matcher(pdbId).matches());
}

private static Stream<String> invalidPdbIds() {
return Stream.of("1ab_", "1ab", "1abcd", "abcd", "1ab-");
}

@Test
void matchesPdbId() {
assertTrue(IdentifierRegex.PDB_ID_REGEX.matcher("1abc").matches());
void matchesExtendedPdbId() {
assertTrue(IdentifierRegex.EXT_PDB_ID_REGEX.matcher("pdb_00001abc").matches());
}

@ParameterizedTest(name = "does not match extended PDB ID: {0}")
@MethodSource("invalidExtendedPdbIds")
void doesNotMatchInvalidExtendedPdbId(String extPdbId) {
assertFalse(IdentifierRegex.EXT_PDB_ID_REGEX.matcher(extPdbId).matches());
}

private static Stream<String> invalidExtendedPdbIds() {
return Stream.of(
"PDB_00001ABC",
"pdb_00001AbC",
"pdb_00001ab",
"pdb_00001abcd",
"1abc",
"pdb_00001ab-"
);
}

@ParameterizedTest(name = "matches PDB ID with optional suffix: {0}")
Expand All @@ -20,13 +60,95 @@ void matchesPdbIdOptionallyWithValidSuffix(String pdbId) {
assertTrue(IdentifierRegex.PDB_ID_WITH_SUFFIX_REGEX.matcher(pdbId).matches());
}

@ParameterizedTest(name = "matches extended PDB ID with optional suffix: {0}")
@MethodSource("validExtPdbIdsWithOptionalSuffixes")
void matchesExtPdbIdOptionallyWithValidSuffix(String extPdbId) {
assertTrue(IdentifierRegex.EXT_PDB_ID_WITH_SUFFIX_REGEX.matcher(extPdbId).matches());
}

private static Stream<String> validExtPdbIdsWithOptionalSuffixes() {
return Stream.of(
"pdb_00001abc",
"pdb_00001abc" + IdentifierSeparator.ENTITY_SEPARATOR + "2",
"pdb_00001abc" + IdentifierSeparator.ASSEMBLY_SEPARATOR + "1",
"pdb_00001abc" + IdentifierSeparator.ENTITY_INSTANCE_SEPARATOR + "A",
"pdb_00001abc-1" + IdentifierSeparator.INTERFACE_SEPARATOR + "1",
"pdb_00001abc" + IdentifierSeparator.ENTITY_INSTANCE_SEPARATOR + "AA"
);
}

@ParameterizedTest(name = "does not match extended PDB ID with suffix: {0}")
@MethodSource("invalidExtPdbIdsWithSuffixes")
void doesNotMatchInvalidExtPdbIdWithSuffix(String extPdbId) {
assertFalse(IdentifierRegex.EXT_PDB_ID_WITH_SUFFIX_REGEX.matcher(extPdbId).matches());
}

private static Stream<String> invalidExtPdbIdsWithSuffixes() {
return Stream.of(
"PDB_00001ABC" + IdentifierSeparator.ENTITY_SEPARATOR + "2",
"pdb_00001ab" + IdentifierSeparator.ENTITY_SEPARATOR + "2",
"pdb_00001abc" + IdentifierSeparator.ENTITY_SEPARATOR,
"1abc" + IdentifierSeparator.ENTITY_SEPARATOR + "2",
"pdb_00001abc" + IdentifierSeparator.ENTITY_INSTANCE_SEPARATOR + "A_1"
);
}

@ParameterizedTest(name = "does not match PDB ID with suffix: {0}")
@MethodSource("invalidPdbIdsWithSuffixes")
void doesNotMatchInvalidPdbIdWithSuffix(String pdbId) {
assertFalse(IdentifierRegex.PDB_ID_WITH_SUFFIX_REGEX.matcher(pdbId).matches());
}

private static Stream<String> invalidPdbIdsWithSuffixes() {
return Stream.of(
"1abc" + IdentifierSeparator.ASSEMBLY_SEPARATOR + "1_1",
"1abc" + IdentifierSeparator.ENTITY_INSTANCE_SEPARATOR + "A_",
"1abc" + IdentifierSeparator.ASSEMBLY_SEPARATOR
);
}

private static Stream<String> validPdbIdsWithOptionalSuffixes() {
return Stream.of(
"1abc",
"1abc" + IdentifierSeparator.ENTITY_SEPARATOR + "2",
"1abc" + IdentifierSeparator.ASSEMBLY_SEPARATOR + "1",
"1abc" + IdentifierSeparator.ENTITY_INSTANCE_SEPARATOR + "A",
"1abc-1" + IdentifierSeparator.INTERFACE_SEPARATOR + "1"
"1abc-1" + IdentifierSeparator.INTERFACE_SEPARATOR + "1",
"1ABC" + IdentifierSeparator.ENTITY_INSTANCE_SEPARATOR + "AA"
);
}
}

@ParameterizedTest(name = "isLegacyPdbId({0})")
@MethodSource("validPdbIds")
void isLegacyPdbIdTrueForLegacyIds(String id) {
assertTrue(IdentifierRegex.isLegacyPdbId(id));
assertTrue(IdentifierRegex.isAnyPdbId(id));
assertFalse(IdentifierRegex.isExtPdbId(id));
}

@ParameterizedTest(name = "isExtPdbId({0})")
@ValueSource(strings = {"pdb_00001abc", "pdb_12345678", "pdb_abcdefgh"})
void isExtPdbIdTrueForExtendedIds(String id) {
assertTrue(IdentifierRegex.isExtPdbId(id));
assertTrue(IdentifierRegex.isAnyPdbId(id));
assertFalse(IdentifierRegex.isLegacyPdbId(id));
}

@ParameterizedTest(name = "no flavour matches: {0}")
@NullSource
@ValueSource(strings = {
"",
"1ab",
"1abcd",
"PDB_00001ABC",
"pdb_00001AbC",
"pdb_00001ab",
"1abc" + "_" + "2",
"pdb_00001abc" + "_" + "2"
})
void noFlavourMatchesInvalidOrSuffixedIds(String id) {
assertFalse(IdentifierRegex.isLegacyPdbId(id));
assertFalse(IdentifierRegex.isExtPdbId(id));
assertFalse(IdentifierRegex.isAnyPdbId(id));
}
}
Loading