Replace enums with charsequence sub classes
This commit is contained in:
parent
cb7e0e4c35
commit
ddd0b27a5c
7 changed files with 109 additions and 129 deletions
|
@ -0,0 +1,63 @@
|
||||||
|
package de.vanitasvitae.imi.codes;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
public abstract class BoundedCharSequence implements CharSequence {
|
||||||
|
|
||||||
|
private final Pattern regex;
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
protected BoundedCharSequence(Pattern regex, String value) throws InvalidOptionException {
|
||||||
|
this.regex = regex;
|
||||||
|
if (!matchPattern(value)) {
|
||||||
|
throw new InvalidOptionException("Invalid input.");
|
||||||
|
}
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchPattern(String input) {
|
||||||
|
return regex.matcher(input).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int length() {
|
||||||
|
return value.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char charAt(int i) {
|
||||||
|
return value.charAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence subSequence(int a, int b) {
|
||||||
|
return value.subSequence(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IntStream chars() {
|
||||||
|
return value.chars();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IntStream codePoints() {
|
||||||
|
return value.codePoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
BoundedCharSequence b = (BoundedCharSequence) o;
|
||||||
|
return toString().equals(b.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,16 +9,6 @@ public class InputValidator {
|
||||||
public static final String REGEX_ALPHABET = "[a-zA-Z0-9]";
|
public static final String REGEX_ALPHABET = "[a-zA-Z0-9]";
|
||||||
public static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
public static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
|
||||||
/*
|
|
||||||
REGEX pattern for study numbers. Allowed are all 3-letter numbers from the alphabet [a-zA-Z0-9].
|
|
||||||
*/
|
|
||||||
private static final Pattern STUDY_NUMBER_MATCHER = Pattern.compile(REGEX_ALPHABET +"{3}");
|
|
||||||
|
|
||||||
/*
|
|
||||||
REGEX pattern for sample numbers. Allowed are four-letter numbers from the alphabet [a-zA-Z0-9].
|
|
||||||
*/
|
|
||||||
private static final Pattern SAMPLE_NUMBER_MATCHER = Pattern.compile(REGEX_ALPHABET +"{4}");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate a given study number. Valid study numbers are 3-letter codes from the alphabet [a-zA-Z0-9].
|
* Validate a given study number. Valid study numbers are 3-letter codes from the alphabet [a-zA-Z0-9].
|
||||||
*
|
*
|
||||||
|
@ -27,12 +17,8 @@ public class InputValidator {
|
||||||
*
|
*
|
||||||
* @throws InvalidOptionException if the input String doesn't match the regex.
|
* @throws InvalidOptionException if the input String doesn't match the regex.
|
||||||
*/
|
*/
|
||||||
public static String validateStudyNumber(String in) throws InvalidOptionException {
|
public static StudyNumber validateStudyNumber(String in) throws InvalidOptionException {
|
||||||
if (!STUDY_NUMBER_MATCHER.matcher(in).matches()) {
|
return new StudyNumber(in);
|
||||||
throw new InvalidOptionException("Study number must be a three digit number from the alphabet [a-zA-Z0-9].");
|
|
||||||
}
|
|
||||||
|
|
||||||
return in;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,12 +31,7 @@ public class InputValidator {
|
||||||
* @throws NullPointerException in case the given String is {@code null}.
|
* @throws NullPointerException in case the given String is {@code null}.
|
||||||
*/
|
*/
|
||||||
public static SampleType validateSampleType(String in) throws InvalidOptionException {
|
public static SampleType validateSampleType(String in) throws InvalidOptionException {
|
||||||
try {
|
return new SampleType(in);
|
||||||
return SampleType.getEnum(in);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
String message = "Invalid sample type \"" + in + "\".";
|
|
||||||
throw new InvalidOptionException(message, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class Main {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
String studyNumber;
|
StudyNumber studyNumber;
|
||||||
SampleType sampleType;
|
SampleType sampleType;
|
||||||
int numberOfCodes;
|
int numberOfCodes;
|
||||||
File outputPath;
|
File outputPath;
|
||||||
|
@ -58,7 +58,7 @@ public class Main {
|
||||||
+ " output: " + outputPath + " browser: " + externalBrowser);
|
+ " output: " + outputPath + " browser: " + externalBrowser);
|
||||||
|
|
||||||
for (int i = 0; i < numberOfCodes; i++) {
|
for (int i = 0; i < numberOfCodes; i++) {
|
||||||
System.out.format("%s%04d%n", studyNumber + sampleType, i);
|
System.out.format("%s%04d%n", studyNumber.toString() + sampleType, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,62 +1,22 @@
|
||||||
package de.vanitasvitae.imi.codes;
|
package de.vanitasvitae.imi.codes;
|
||||||
|
|
||||||
public enum SampleType {
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
// Hard coded values. Prepended with an underscore to allow numeric values (eg. _2).
|
|
||||||
_b('b', "Blood"),
|
|
||||||
_u('u', "Urine"),
|
|
||||||
;
|
|
||||||
|
|
||||||
// Members of the enum
|
|
||||||
|
|
||||||
private final char name;
|
|
||||||
private final String description;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a SampleType enum which is described by a name (one letter from the alphabet [a-zA-Z0-9]) and a
|
* Sub-class of {@link CharSequence}, which can only represent char sequences of length 1 from the alphabet [a-zA-Z0-9].
|
||||||
* human readable description.
|
|
||||||
*
|
|
||||||
* @param name one letter name
|
|
||||||
* @param description description
|
|
||||||
*/
|
*/
|
||||||
SampleType(char name, String description) {
|
public class SampleType extends BoundedCharSequence {
|
||||||
this.name = name;
|
|
||||||
this.description = description;
|
private static final Pattern REGEX = Pattern.compile(InputValidator.REGEX_ALPHABET);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override {@link Enum#toString()} in order to return the name of the enum instead of its value.
|
* Create a new {@link SampleType} sequence. The constructor checks, whether the input String is of length 1 and from
|
||||||
|
* the alphabet [a-zA-Z9-0].
|
||||||
*
|
*
|
||||||
* @return name
|
* @param value input string
|
||||||
|
* @throws InvalidOptionException if the input doesn't match the regex.
|
||||||
*/
|
*/
|
||||||
@Override
|
public SampleType(String value) throws InvalidOptionException {
|
||||||
public String toString() {
|
super(REGEX, value);
|
||||||
return "" + name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the human readable description of the enum.
|
|
||||||
*/
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replacement method for {@link #valueOf(String)}. You MUST use this method instead, as we have to escape
|
|
||||||
* the names of the enums with a underscore in order to allow for numerals to be used as enum name (for example "_0").
|
|
||||||
* This method takes a string value (eg. "u") and returns the corresponding enum ("_u").
|
|
||||||
*
|
|
||||||
* @param name name of the enum without an underscore.
|
|
||||||
* @return enum corresponding to the name.
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if no matching enum was found.
|
|
||||||
*/
|
|
||||||
public static SampleType getEnum(String name) {
|
|
||||||
for (SampleType s : SampleType.values()) {
|
|
||||||
if (s.toString().equals(name)) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("No SampleType found for name \"" + name + "\".");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
22
src/main/java/de/vanitasvitae/imi/codes/StudyNumber.java
Normal file
22
src/main/java/de/vanitasvitae/imi/codes/StudyNumber.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package de.vanitasvitae.imi.codes;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sub-class of {@link CharSequence}, which can only represent char sequences of length 3 from the alphabet [a-zA-Z0-9].
|
||||||
|
*/
|
||||||
|
public class StudyNumber extends BoundedCharSequence {
|
||||||
|
|
||||||
|
private static final Pattern REGEX = Pattern.compile(InputValidator.REGEX_ALPHABET + "{3}");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link StudyNumber} sequence. The constructor checks, whether the input String is of length 3 and from
|
||||||
|
* the alphabet [a-zA-Z9-0].
|
||||||
|
*
|
||||||
|
* @param value input string
|
||||||
|
* @throws InvalidOptionException if the input doesn't match the regex
|
||||||
|
*/
|
||||||
|
protected StudyNumber(String value) throws InvalidOptionException {
|
||||||
|
super(REGEX, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,8 @@ package de.vanitasvitae.imi.codes;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -19,7 +21,7 @@ public class InputValidatorTest {
|
||||||
public void testValidateStudyNumber() throws InvalidOptionException {
|
public void testValidateStudyNumber() throws InvalidOptionException {
|
||||||
String in = generateTestStringOfLength(3);
|
String in = generateTestStringOfLength(3);
|
||||||
assertEquals("validateStudyNumber must accept valid three digit inputs from the alphabet [a-zA-Z0-9].",
|
assertEquals("validateStudyNumber must accept valid three digit inputs from the alphabet [a-zA-Z0-9].",
|
||||||
in, InputValidator.validateStudyNumber(in));
|
new StudyNumber(in), InputValidator.validateStudyNumber(in));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,24 +86,15 @@ public class InputValidatorTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testValidateSampleType() throws InvalidOptionException {
|
public void testValidateSampleType() throws InvalidOptionException {
|
||||||
for (SampleType t : SampleType.values()) {
|
List<SampleType> sampleSampleTypes = new ArrayList<>();
|
||||||
|
sampleSampleTypes.add(new SampleType("u"));
|
||||||
|
sampleSampleTypes.add(new SampleType("b"));
|
||||||
|
for (SampleType t : sampleSampleTypes) {
|
||||||
assertEquals("validateSampleType didn't return expected value for input \"" + t.toString() + "\"",
|
assertEquals("validateSampleType didn't return expected value for input \"" + t.toString() + "\"",
|
||||||
t, InputValidator.validateSampleType(t.toString()));
|
t, InputValidator.validateSampleType(t.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test, whether {@link InputValidator#validateSampleType(String)} throws a {@link InvalidOptionException} for
|
|
||||||
* unknown sample types (in this case {@code x}).
|
|
||||||
*
|
|
||||||
* @throws InvalidOptionException expected
|
|
||||||
*/
|
|
||||||
@Test(expected = InvalidOptionException.class)
|
|
||||||
public void testValidateSampleTypeFailsForUnknownInputs() throws InvalidOptionException {
|
|
||||||
// -> InvalidOptionException
|
|
||||||
InputValidator.validateSampleType("x");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test, whether {@link InputValidator#validateNumberOfCodes(String)} returns the expected {@link Integer}.
|
* Test, whether {@link InputValidator#validateNumberOfCodes(String)} returns the expected {@link Integer}.
|
||||||
* In this method, posit
|
* In this method, posit
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
package de.vanitasvitae.imi.codes;
|
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
|
||||||
import static junit.framework.TestCase.assertTrue;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test functionality of the {@link SampleType} enum.
|
|
||||||
*/
|
|
||||||
public class SampleTypeTest {
|
|
||||||
|
|
||||||
private static final Pattern SAMPLE_TYPE_VALUE_PATTERN = Pattern.compile(InputValidator.REGEX_ALPHABET);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test, whether all {@link SampleType} names are of length 1 and from the alphabet [a-zA-Z0-9].
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testSampleTypeNames() {
|
|
||||||
for (SampleType t : SampleType.values()) {
|
|
||||||
assertTrue("SampleType value names must be one letter from the alphabet [a-zA-Z0-9].",
|
|
||||||
SAMPLE_TYPE_VALUE_PATTERN.matcher(t.toString()).matches());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test, whether {@link SampleType#getEnum(String)} correctly maps {@link SampleType#name} to the corresponding
|
|
||||||
* values.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testGetEnum() {
|
|
||||||
for (SampleType t : SampleType.values()) {
|
|
||||||
assertEquals("getEnum must return the SampleType, which corresponds to the given name.",
|
|
||||||
t, SampleType.getEnum(t.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue