diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 37677eb9863..588a4c2cfaf 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -11,6 +11,7 @@ public class CliSyntax { public static final Prefix PREFIX_EMAIL = new Prefix("e/"); public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); + public static final Prefix PREFIX_UNITNUMBER = new Prefix("unitNo/"); public static final Prefix PREFIX_BLOCK = new Prefix("blk/"); public static final Prefix PREFIX_POSTALCODE = new Prefix("postal/"); } diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index 795567ea741..be3e9092af8 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -11,6 +11,7 @@ import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.house.Block; import seedu.address.model.house.PostalCode; +import seedu.address.model.house.UnitNumber; import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; @@ -125,6 +126,20 @@ public static Set parseTags(Collection tags) throws ParseException return tagSet; } + /** + * Parses a {@code String unitNumber} into a {@code unitNumber}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code unitNumber} is invalid. + */ + public static UnitNumber parseUnitNumber(String unitNumber) throws ParseException { + requireNonNull(unitNumber); + String trimmedLevel = unitNumber.trim(); + if (!UnitNumber.isValidUnitNumber(trimmedLevel)) { + throw new ParseException(UnitNumber.MESSAGE_CONSTRAINTS); + } + return new UnitNumber(trimmedLevel); + } /** * Parses a {@code String block} into an {@code Block}. * Leading and trailing whitespaces will be trimmed. diff --git a/src/main/java/seedu/address/model/house/UnitNumber.java b/src/main/java/seedu/address/model/house/UnitNumber.java new file mode 100644 index 00000000000..2e2713f7d8e --- /dev/null +++ b/src/main/java/seedu/address/model/house/UnitNumber.java @@ -0,0 +1,64 @@ +package seedu.address.model.house; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a House's unit number in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidUnitNumber(String)} + */ +public class UnitNumber { + + public static final String MESSAGE_CONSTRAINTS = + "The unit number should only contain numbers, it should be at least 1 digit " + + "and at most 3 digits long, and cannot be '0', '00' or '000'."; + public static final String VALIDATION_REGEX = "\\d{1,3}"; + public static final String ZERO_REGEX = "^0+$"; + + public final String value; + + /** + * Constructs a {@code UnitNumber}. + * + * @param unitNumber A valid unit number. + */ + public UnitNumber(String unitNumber) { + requireNonNull(unitNumber); + checkArgument(isValidUnitNumber(unitNumber), MESSAGE_CONSTRAINTS); + value = unitNumber; + } + + /** + * Returns true if a given string is a valid unit number. + * + * @param test The string to test. + * @return true if the test matches the VALIDATION_REGEX and is not "0". + */ + public static boolean isValidUnitNumber(String test) { + return test.matches(VALIDATION_REGEX) && !test.matches(ZERO_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (!(other instanceof UnitNumber)) { + return false; + } + + UnitNumber otherUnitNumber = (UnitNumber) other; + return value.equals(otherUnitNumber.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java index 76e3df6f996..f363378c13b 100644 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java @@ -16,6 +16,7 @@ import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.house.Block; import seedu.address.model.house.PostalCode; +import seedu.address.model.house.UnitNumber; import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; @@ -28,6 +29,7 @@ public class ParserUtilTest { private static final String INVALID_ADDRESS = " "; private static final String INVALID_EMAIL = "example.com"; private static final String INVALID_TAG = "#friend"; + private static final String INVALID_UNIT_NUMBER = "1234"; private static final String INVALID_BLOCK = "12a34"; private static final String INVALID_POSTALCODE = "5678990"; @@ -38,6 +40,7 @@ public class ParserUtilTest { private static final String VALID_POSTALCODE = "654321"; private static final String VALID_TAG_1 = "friend"; private static final String VALID_TAG_2 = "neighbour"; + private static final String VALID_UNIT_NUMBER = "123"; private static final String VALID_BLOCK = "205A"; private static final String WHITESPACE = " \t\r\n"; @@ -224,6 +227,28 @@ public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { } @Test + public void parseUnitNumber_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseUnitNumber((String) null)); + } + + @Test + public void parseUnitNumber_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseUnitNumber(INVALID_UNIT_NUMBER)); + } + + @Test + public void parseUnitNumber_validValueWithoutWhitespace_returnsUnitNumber() throws Exception { + UnitNumber expectedUnitNumber = new UnitNumber(VALID_UNIT_NUMBER); + assertEquals(expectedUnitNumber, ParserUtil.parseUnitNumber(VALID_UNIT_NUMBER)); + } + + @Test + public void parseUnitNumber_validValueWithWhitespace_returnsTrimmedUnitNumber() throws Exception { + String unitNumberWithWhitespace = WHITESPACE + VALID_UNIT_NUMBER + WHITESPACE; + UnitNumber expectedUnitNumber = new UnitNumber(VALID_UNIT_NUMBER); + assertEquals(expectedUnitNumber, ParserUtil.parseUnitNumber(unitNumberWithWhitespace)); + } + public void parseBlock_null_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> ParserUtil.parseBlock((String) null)); } diff --git a/src/test/java/seedu/address/model/house/UnitNumberTest.java b/src/test/java/seedu/address/model/house/UnitNumberTest.java new file mode 100644 index 00000000000..eaeb7b3df5e --- /dev/null +++ b/src/test/java/seedu/address/model/house/UnitNumberTest.java @@ -0,0 +1,64 @@ +package seedu.address.model.house; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class UnitNumberTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new UnitNumber(null)); + } + + @Test + public void constructor_invalidUnitNumber_throwsIllegalArgumentException() { + String invalidUnitNumber = "abcd"; + assertThrows(IllegalArgumentException.class, () -> new UnitNumber(invalidUnitNumber)); + } + + @Test + public void isValidUnitNumber() { + // null unit number + assertThrows(NullPointerException.class, () -> UnitNumber.isValidUnitNumber(null)); + + // invalid unit numbers + assertFalse(UnitNumber.isValidUnitNumber("0")); // '0' is invalid + assertFalse(UnitNumber.isValidUnitNumber("00")); // '00' is invalid + assertFalse(UnitNumber.isValidUnitNumber("000")); // '000' is invalid + assertFalse(UnitNumber.isValidUnitNumber("")); // empty string + assertFalse(UnitNumber.isValidUnitNumber(" ")); // spaces only + assertFalse(UnitNumber.isValidUnitNumber("1234")); // more than 3 digits + assertFalse(UnitNumber.isValidUnitNumber("ab")); // non-numeric + assertFalse(UnitNumber.isValidUnitNumber("1a2")); // alphabets within digits + + // valid unit numbers + assertTrue(UnitNumber.isValidUnitNumber("1")); // minimum valid number + assertTrue(UnitNumber.isValidUnitNumber("01")); // leading 0 is allowed + assertTrue(UnitNumber.isValidUnitNumber("10")); // 2 digits + assertTrue(UnitNumber.isValidUnitNumber("001")); // 3 digits + assertTrue(UnitNumber.isValidUnitNumber("999")); // maximum valid number + } + + @Test + public void equals() { + UnitNumber unitNumber = new UnitNumber("12"); + + // same values -> returns true + assertTrue(unitNumber.equals(new UnitNumber("12"))); + + // same object -> returns true + assertTrue(unitNumber.equals(unitNumber)); + + // null -> returns false + assertFalse(unitNumber.equals(null)); + + // different types -> returns false + assertFalse(unitNumber.equals(5.0f)); + + // different unit number -> returns false + assertFalse(unitNumber.equals(new UnitNumber("34"))); + } +}