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 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].
|
||||
*
|
||||
|
@ -27,12 +17,8 @@ public class InputValidator {
|
|||
*
|
||||
* @throws InvalidOptionException if the input String doesn't match the regex.
|
||||
*/
|
||||
public static String validateStudyNumber(String in) throws InvalidOptionException {
|
||||
if (!STUDY_NUMBER_MATCHER.matcher(in).matches()) {
|
||||
throw new InvalidOptionException("Study number must be a three digit number from the alphabet [a-zA-Z0-9].");
|
||||
}
|
||||
|
||||
return in;
|
||||
public static StudyNumber validateStudyNumber(String in) throws InvalidOptionException {
|
||||
return new StudyNumber(in);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,12 +31,7 @@ public class InputValidator {
|
|||
* @throws NullPointerException in case the given String is {@code null}.
|
||||
*/
|
||||
public static SampleType validateSampleType(String in) throws InvalidOptionException {
|
||||
try {
|
||||
return SampleType.getEnum(in);
|
||||
} catch (IllegalArgumentException e) {
|
||||
String message = "Invalid sample type \"" + in + "\".";
|
||||
throw new InvalidOptionException(message, e);
|
||||
}
|
||||
return new SampleType(in);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -35,7 +35,7 @@ public class Main {
|
|||
}
|
||||
|
||||
// Parse arguments
|
||||
String studyNumber;
|
||||
StudyNumber studyNumber;
|
||||
SampleType sampleType;
|
||||
int numberOfCodes;
|
||||
File outputPath;
|
||||
|
@ -58,7 +58,7 @@ public class Main {
|
|||
+ " output: " + outputPath + " browser: " + externalBrowser);
|
||||
|
||||
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;
|
||||
|
||||
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"),
|
||||
;
|
||||
/**
|
||||
* Sub-class of {@link CharSequence}, which can only represent char sequences of length 1 from the alphabet [a-zA-Z0-9].
|
||||
*/
|
||||
public class SampleType extends BoundedCharSequence {
|
||||
|
||||
// Members of the enum
|
||||
|
||||
private final char name;
|
||||
private final String description;
|
||||
private static final Pattern REGEX = Pattern.compile(InputValidator.REGEX_ALPHABET);
|
||||
|
||||
/**
|
||||
* Create a SampleType enum which is described by a name (one letter from the alphabet [a-zA-Z0-9]) and a
|
||||
* human readable description.
|
||||
* 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].
|
||||
*
|
||||
* @param name one letter name
|
||||
* @param description description
|
||||
* @param value input string
|
||||
* @throws InvalidOptionException if the input doesn't match the regex.
|
||||
*/
|
||||
SampleType(char name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override {@link Enum#toString()} in order to return the name of the enum instead of its value.
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
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 + "\".");
|
||||
public SampleType(String value) throws InvalidOptionException {
|
||||
super(REGEX, value);
|
||||
}
|
||||
}
|
||||
|
|
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Test;
|
||||
|
@ -19,7 +21,7 @@ public class InputValidatorTest {
|
|||
public void testValidateStudyNumber() throws InvalidOptionException {
|
||||
String in = generateTestStringOfLength(3);
|
||||
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
|
||||
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() + "\"",
|
||||
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}.
|
||||
* 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