From ddd0b27a5c53d91d467b2d48c00c20dc4a4a9a87 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 15 Nov 2018 17:54:34 +0100 Subject: [PATCH] Replace enums with charsequence sub classes --- .../imi/codes/BoundedCharSequence.java | 63 ++++++++++++++++++ .../imi/codes/InputValidator.java | 25 +------- .../java/de/vanitasvitae/imi/codes/Main.java | 4 +- .../de/vanitasvitae/imi/codes/SampleType.java | 64 ++++--------------- .../vanitasvitae/imi/codes/StudyNumber.java | 22 +++++++ .../imi/codes/InputValidatorTest.java | 21 ++---- .../imi/codes/SampleTypeTest.java | 39 ----------- 7 files changed, 109 insertions(+), 129 deletions(-) create mode 100644 src/main/java/de/vanitasvitae/imi/codes/BoundedCharSequence.java create mode 100644 src/main/java/de/vanitasvitae/imi/codes/StudyNumber.java delete mode 100644 src/test/java/de/vanitasvitae/imi/codes/SampleTypeTest.java diff --git a/src/main/java/de/vanitasvitae/imi/codes/BoundedCharSequence.java b/src/main/java/de/vanitasvitae/imi/codes/BoundedCharSequence.java new file mode 100644 index 0000000..236d9ef --- /dev/null +++ b/src/main/java/de/vanitasvitae/imi/codes/BoundedCharSequence.java @@ -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; + } +} diff --git a/src/main/java/de/vanitasvitae/imi/codes/InputValidator.java b/src/main/java/de/vanitasvitae/imi/codes/InputValidator.java index ca4102e..780bb01 100644 --- a/src/main/java/de/vanitasvitae/imi/codes/InputValidator.java +++ b/src/main/java/de/vanitasvitae/imi/codes/InputValidator.java @@ -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); } /** diff --git a/src/main/java/de/vanitasvitae/imi/codes/Main.java b/src/main/java/de/vanitasvitae/imi/codes/Main.java index 7d6ebd2..476092e 100644 --- a/src/main/java/de/vanitasvitae/imi/codes/Main.java +++ b/src/main/java/de/vanitasvitae/imi/codes/Main.java @@ -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); } } diff --git a/src/main/java/de/vanitasvitae/imi/codes/SampleType.java b/src/main/java/de/vanitasvitae/imi/codes/SampleType.java index 8eb29fc..2f5b0fd 100644 --- a/src/main/java/de/vanitasvitae/imi/codes/SampleType.java +++ b/src/main/java/de/vanitasvitae/imi/codes/SampleType.java @@ -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); } } diff --git a/src/main/java/de/vanitasvitae/imi/codes/StudyNumber.java b/src/main/java/de/vanitasvitae/imi/codes/StudyNumber.java new file mode 100644 index 0000000..df860a5 --- /dev/null +++ b/src/main/java/de/vanitasvitae/imi/codes/StudyNumber.java @@ -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); + } +} diff --git a/src/test/java/de/vanitasvitae/imi/codes/InputValidatorTest.java b/src/test/java/de/vanitasvitae/imi/codes/InputValidatorTest.java index 261615d..d2ebf20 100644 --- a/src/test/java/de/vanitasvitae/imi/codes/InputValidatorTest.java +++ b/src/test/java/de/vanitasvitae/imi/codes/InputValidatorTest.java @@ -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 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 diff --git a/src/test/java/de/vanitasvitae/imi/codes/SampleTypeTest.java b/src/test/java/de/vanitasvitae/imi/codes/SampleTypeTest.java deleted file mode 100644 index 4ef5d84..0000000 --- a/src/test/java/de/vanitasvitae/imi/codes/SampleTypeTest.java +++ /dev/null @@ -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())); - } - } -}