Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance arg multimap #192

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
4 changes: 2 additions & 2 deletions src/main/java/seedu/address/commons/core/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ public class Messages {
public static final String MESSAGE_INVALID_ITEM_DISPLAYED_INDEX = "The item index provided is invalid";
public static final String MESSAGE_ITEMS_LISTED_OVERVIEW = "%1$d items listed!";
public static final String MESSAGE_INVALID_COUNT_INTEGER = "The count provided must be positive!";
public static final String MESSAGE_INVALID_COUNT_FORMAT = "The count provided must be integer!";
public static final String MESSAGE_INVALID_COUNT_FORMAT = "The count provided must be an integer!\n"
+ "Minimum: 1\n" + "Maximum: 2,147,483,647";
public static final String MESSAGE_INVALID_COUNT_INDEX = "The index provided must be a number (can't be >1 number)"
+ " and cannot be 0 or negative!";
public static final String MESSAGE_INVALID_PRICE_FORMAT = "Prices provided must be numerical values!";
Expand All @@ -19,5 +20,4 @@ public class Messages {
public static final String MESSAGE_INVALID_ID_FORMAT = "The id provided must be integer!";
public static final String MESSAGE_INVALID_ID_LENGTH_AND_SIGN = "The id provided must be positive"
+ " and at most 6 digits!";
public static final String MESSAGE_NAME_SPECIFIED_TWICE = "Name field is specified twice!";
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package seedu.address.logic.parser;

import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.commons.core.Messages.MESSAGE_NAME_SPECIFIED_TWICE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_COSTPRICE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_COUNT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ID;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_SALESPRICE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;

Expand All @@ -24,18 +22,14 @@ public class AddToOrderCommandParser implements Parser<AddToOrderCommand> {
@Override
public AddToOrderCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap =
ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_ID, PREFIX_COUNT, PREFIX_TAG,
ArgumentTokenizer.tokenize(args, PREFIX_ID, PREFIX_COUNT, PREFIX_TAG,
PREFIX_COSTPRICE, PREFIX_SALESPRICE);

if (argMultimap.getValue(PREFIX_ID).isEmpty() && argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddToOrderCommand.MESSAGE_USAGE));
}

ItemDescriptor toAddDescriptor = new ItemDescriptor();
// if both name flag and prefix are present
if (!argMultimap.getPreamble().isEmpty() && argMultimap.getValue(PREFIX_NAME).isPresent()) {
throw new ParseException(MESSAGE_NAME_SPECIFIED_TWICE + "\n" + AddToOrderCommand.MESSAGE_USAGE);
}

// Parse preamble
if (!argMultimap.getPreamble().isEmpty()) {
Expand Down
67 changes: 26 additions & 41 deletions src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package seedu.address.logic.parser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import seedu.address.logic.parser.exceptions.ParseException;

/**
* Tokenizes arguments string of the form: {@code preamble <prefix>value <prefix>value ...}<br>
Expand All @@ -15,15 +17,19 @@
*/
public class ArgumentTokenizer {

// Regex matching any series of non-whitespace characters followed by "/"
public static final String PREFIX_REGEX = "[^\\s]*/";

/**
* Tokenizes an arguments string and returns an {@code ArgumentMultimap} object that maps prefixes to their
* respective argument values. Only the given prefixes will be recognized in the arguments string.
* respective argument values.
*
* @param argsString Arguments string of the form: {@code preamble <prefix>value <prefix>value ...}
* @param prefixes Prefixes to tokenize the arguments string with
* @param prefixes Expected prefixes
* @return ArgumentMultimap object that maps prefixes to their arguments
* @throws ParseException if an unexpected prefix is detected
*/
public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) {
public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) throws ParseException {
List<PrefixPosition> positions = findAllPrefixPositions(argsString, prefixes);
return extractArguments(argsString, positions);
}
Expand All @@ -32,47 +38,26 @@ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) {
* Finds all zero-based prefix positions in the given arguments string.
*
* @param argsString Arguments string of the form: {@code preamble <prefix>value <prefix>value ...}
* @param prefixes Prefixes to find in the arguments string
* @param prefixes Expected prefixes
* @return List of zero-based prefix positions in the given arguments string
*/
private static List<PrefixPosition> findAllPrefixPositions(String argsString, Prefix... prefixes) {
return Arrays.stream(prefixes)
.flatMap(prefix -> findPrefixPositions(argsString, prefix).stream())
.collect(Collectors.toList());
}
private static List<PrefixPosition> findAllPrefixPositions(String argsString, Prefix... prefixes)
throws ParseException {
Matcher m = Pattern.compile(PREFIX_REGEX).matcher(argsString);

/**
* {@see findAllPrefixPositions}
*/
private static List<PrefixPosition> findPrefixPositions(String argsString, Prefix prefix) {
List<PrefixPosition> positions = new ArrayList<>();

int prefixPosition = findPrefixPosition(argsString, prefix.getPrefix(), 0);
while (prefixPosition != -1) {
PrefixPosition extendedPrefix = new PrefixPosition(prefix, prefixPosition);
positions.add(extendedPrefix);
prefixPosition = findPrefixPosition(argsString, prefix.getPrefix(), prefixPosition);
}
List<PrefixPosition> prefixPositionList = new ArrayList<>();
while (m.find()) {
Prefix found = new Prefix(m.group());

return positions;
}
// If prefix is unexpected, throw parse exception
if (!List.of(prefixes).contains(found)) {
throw new ParseException(String.format("Unexpected prefix identified: %s", m.group()));
}

/**
* Returns the index of the first occurrence of {@code prefix} in
* {@code argsString} starting from index {@code fromIndex}. An occurrence
* is valid if there is a whitespace before {@code prefix}. Returns -1 if no
* such occurrence can be found.
*
* E.g if {@code argsString} = "e/hip/900", {@code prefix} = "p/" and
* {@code fromIndex} = 0, this method returns -1 as there are no valid
* occurrences of "p/" with whitespace before it. However, if
* {@code argsString} = "e/hi p/900", {@code prefix} = "p/" and
* {@code fromIndex} = 0, this method returns 5.
*/
private static int findPrefixPosition(String argsString, String prefix, int fromIndex) {
int prefixIndex = argsString.indexOf(" " + prefix, fromIndex);
return prefixIndex == -1 ? -1
: prefixIndex + 1; // +1 as offset for whitespace
prefixPositionList.add(new PrefixPosition(found, m.start()));
}

return prefixPositionList;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@ public FindCommand parse(String args) throws ParseException {

// Add name predicate if name(s) specified
if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
predicates.add(
new NameContainsKeywordsPredicate(argMultimap.getAllValues(PREFIX_NAME))
);
predicates.add(new NameContainsKeywordsPredicate(
argMultimap.getAllValues(PREFIX_NAME)));
}

// Add id predicate if id(s) specified
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package seedu.address.logic.parser;

import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.commons.core.Messages.MESSAGE_NAME_SPECIFIED_TWICE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_COSTPRICE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_COUNT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_ID;
Expand Down Expand Up @@ -32,10 +31,6 @@ public RemoveCommand parse(String args) throws ParseException {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, RemoveCommand.MESSAGE_USAGE));
}

if (argMultimap.getValue(PREFIX_NAME).isPresent() && !argMultimap.getPreamble().isEmpty()) {
throw new ParseException(String.format(MESSAGE_NAME_SPECIFIED_TWICE, RemoveCommand.MESSAGE_USAGE));
}

ItemDescriptor toRemoveDescriptor = new ItemDescriptor();

// Parse name
Expand Down
15 changes: 0 additions & 15 deletions src/test/java/seedu/address/logic/commands/CommandTestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,10 @@ public class CommandTestUtil {

public static final String VALID_NAME_BAGEL = "Bagel";
public static final String VALID_NAME_DONUT = "Donut";
public static final String VALID_NAME_100PLUS = "100Plus";
public static final String VALID_NAME_H20 = "H20";
public static final String VALID_ID_BAGEL = "094021";
public static final String VALID_ID_DONUT = "789013";
public static final String VALID_COUNT_BAGEL = "5";
public static final String VALID_COUNT_DONUT = "6";
public static final String VALID_COUNT_ONE = "1";
public static final String VALID_COSTPRICE_BAGEL = "5.0";
public static final String VALID_COSTPRICE_DONUT = "6.0";
public static final String VALID_SALESPRICE_BAGEL = "6.0";
Expand All @@ -52,17 +49,12 @@ public class CommandTestUtil {
public static final String ID_DESC_DONUT = " " + PREFIX_ID + VALID_ID_DONUT;
public static final String COUNT_DESC_BAGEL = " " + PREFIX_COUNT + VALID_COUNT_BAGEL;
public static final String COUNT_DESC_DONUT = " " + PREFIX_COUNT + VALID_COUNT_DONUT;
public static final String COUNT_DESC_ONE = " " + PREFIX_COUNT + VALID_COUNT_ONE;
public static final String COUNT_DESC_MAX_INT = " " + PREFIX_COUNT + String.valueOf(Integer.MAX_VALUE);
public static final String SALESPRICE_DESC_BAGEL = " " + PREFIX_SALESPRICE + VALID_SALESPRICE_BAGEL;
public static final String SALESPRICE_DESC_DONUT = " " + PREFIX_SALESPRICE + VALID_SALESPRICE_DONUT;
public static final String COSTPRICE_DESC_BAGEL = " " + PREFIX_COSTPRICE + VALID_COSTPRICE_BAGEL;
public static final String COSTPRICE_DESC_DONUT = " " + PREFIX_COSTPRICE + VALID_COSTPRICE_DONUT;
public static final String TAG_DESC_BAKED = " " + PREFIX_TAG + VALID_TAG_BAKED;
public static final String TAG_DESC_POPULAR = " " + PREFIX_TAG + VALID_TAG_POPULAR;

public static final String INVALID_NAME_SPECIAL_CHAR = "Cake$";
public static final String INVALID_NAME_EMPTY_STRING = "";
public static final String INVALID_NAME_DESC =
" " + PREFIX_NAME + INVALID_NAME_SPECIAL_CHAR; // '&' not allowed in names
public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags
Expand All @@ -71,15 +63,8 @@ public class CommandTestUtil {
public static final String INVALID_ID_NEGATIVE_NUMBER = " " + PREFIX_ID + "-123232";
public static final String INVALID_ID_SEVEN_DIGITS = " " + PREFIX_ID + "1234567";
public static final String INVALID_COUNT_LETTER = " " + PREFIX_COUNT + "abc";
public static final String INVALID_COUNT_SPECIAL_CHAR = " " + PREFIX_COUNT + "1234$";
public static final String INVALID_COUNT_ZERO = " " + PREFIX_COUNT + "0";
public static final String INVALID_COUNT_NEGATIVE_VALUE = " " + PREFIX_COUNT + "-1";
public static final String INVALID_COUNT_BEYOND_MAX_INT = " " + PREFIX_COUNT + "2147483648";
public static final String INVALID_SALESPRICE_BAGEL = " " + PREFIX_SALESPRICE + "asdf";
public static final String INVALID_COSTPRICE_BAGEL = " " + PREFIX_COSTPRICE + "asdf";

public static final String PREAMBLE_WHITESPACE = "\t \r \n";
public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";

public static final ItemDescriptor DESC_BAGEL;
public static final ItemDescriptor DESC_DONUT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@


import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.commons.core.Messages.MESSAGE_NAME_SPECIFIED_TWICE;
import static seedu.address.logic.commands.CommandTestUtil.COUNT_DESC_BAGEL;
import static seedu.address.logic.commands.CommandTestUtil.COUNT_DESC_DONUT;
import static seedu.address.logic.commands.CommandTestUtil.COUNT_DESC_MAX_INT;
import static seedu.address.logic.commands.CommandTestUtil.COUNT_DESC_ONE;
import static seedu.address.logic.commands.CommandTestUtil.ID_DESC_BAGEL;
import static seedu.address.logic.commands.CommandTestUtil.ID_DESC_DONUT;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_COUNT_BEYOND_MAX_INT;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_COUNT_LETTER;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_COUNT_NEGATIVE_VALUE;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_COUNT_ZERO;
Expand All @@ -21,13 +17,8 @@
import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_BAKED;
import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_POPULAR;
import static seedu.address.logic.commands.CommandTestUtil.VALID_COUNT_BAGEL;
import static seedu.address.logic.commands.CommandTestUtil.VALID_COUNT_DONUT;
import static seedu.address.logic.commands.CommandTestUtil.VALID_ID_BAGEL;
import static seedu.address.logic.commands.CommandTestUtil.VALID_ID_DONUT;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_100PLUS;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BAGEL;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_DONUT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;

Expand All @@ -42,43 +33,6 @@
public class AddToOrderCommandParserTest {
private AddToOrderCommandParser parser = new AddToOrderCommandParser();

@Test
public void parse_nameEp() {

// EP: valid name: alphabetical string
// Heuristic: other inputs valid EP appear at least once
ItemDescriptor expectedDescriptor = new ItemDescriptorBuilder()
.withName(VALID_NAME_BAGEL)
.withId(VALID_ID_BAGEL)
.withCount(VALID_COUNT_BAGEL)
.build();

assertParseSuccess(parser, VALID_NAME_BAGEL + ID_DESC_BAGEL + COUNT_DESC_BAGEL,
new AddToOrderCommand(expectedDescriptor));

// EP: valid name: alphanumeric string
// Heuristic: other inputs valid EP appear at least once
expectedDescriptor = new ItemDescriptorBuilder()
.withName(VALID_NAME_100PLUS)
.withId(VALID_ID_DONUT)
.withCount(VALID_COUNT_DONUT)
.build();

assertParseSuccess(parser, VALID_NAME_100PLUS + ID_DESC_DONUT + COUNT_DESC_DONUT,
new AddToOrderCommand(expectedDescriptor));

String invalidNameErrorMsg =
"Names should only contain alphanumeric characters and spaces, and it should not be blank";
// EP: invalid name: empty string for name.
//assertParseFailure(parser, INVALID_NAME_EMPTY_STRING + ID_DESC_BAGEL + COUNT_DESC_BAGEL, invalidNameErrorMsg);

// EP: invalid name: special chars involved. e.g."cake$"
//assertParseFailure(parser, INVALID_NAME_SPECIAL_CHAR + ID_DESC_BAGEL + COUNT_DESC_BAGEL, invalidNameErrorMsg);

// EP: invalid name: name comes with prefix.
//assertParseFailure(parser, INVALID_NAME_DESC + ID_DESC_BAGEL + COUNT_DESC_BAGEL, invalidNameErrorMsg);
}

@Test
public void parse_idEp() {

Expand Down Expand Up @@ -111,49 +65,6 @@ public void parse_idEp() {
assertParseFailure(parser, VALID_NAME_BAGEL + INVALID_ID_LETTER + COUNT_DESC_BAGEL, notIntegerErrorMsg);
}

@Test
public void parse_countEp() {
// EP: valid count: boundary value 1
ItemDescriptor expectedDescriptor = new ItemDescriptorBuilder()
.withName(VALID_NAME_BAGEL)
.withId(VALID_ID_BAGEL)
.withCount("1")
.build();

assertParseSuccess(parser, VALID_NAME_BAGEL + ID_DESC_BAGEL + COUNT_DESC_ONE,
new AddToOrderCommand(expectedDescriptor));

// EP: valid count: boundary value Integer.MAX_VALUE
expectedDescriptor = new ItemDescriptorBuilder()
.withName(VALID_NAME_BAGEL)
.withId(VALID_ID_BAGEL)
.withCount(String.valueOf(Integer.MAX_VALUE))
.build();

assertParseSuccess(parser, VALID_NAME_BAGEL + ID_DESC_BAGEL + COUNT_DESC_MAX_INT,
new AddToOrderCommand(expectedDescriptor));

// EP: invalid count: boundary value 0
String zeroCountErrorMsg = "The count provided must be positive!";
assertParseFailure(parser, VALID_NAME_BAGEL + ID_DESC_BAGEL + INVALID_COUNT_ZERO,
zeroCountErrorMsg);

// EP: invalid count: boundary value -1
String negativeCountErrorMsg = "The count provided must be positive!";
assertParseFailure(parser, VALID_NAME_BAGEL + ID_DESC_BAGEL + INVALID_COUNT_NEGATIVE_VALUE,
negativeCountErrorMsg);

// EP: invalid count: boundary value Integer.MAX_VALUE + 1
String notIntegerErrorMsg = "The count provided must be integer!";
assertParseFailure(parser, VALID_NAME_BAGEL + ID_DESC_BAGEL + INVALID_COUNT_BEYOND_MAX_INT,
notIntegerErrorMsg);

// EP: invalid count: not a number
assertParseFailure(parser, VALID_NAME_BAGEL + ID_DESC_BAGEL + INVALID_COUNT_LETTER,
notIntegerErrorMsg);
}


@Test
public void parse_allFieldsPresent_success() {
ItemDescriptor expectedDescriptor = new ItemDescriptorBuilder()
Expand Down Expand Up @@ -221,14 +132,6 @@ public void parse_noNameOrId_failure() {
assertParseFailure(parser, COUNT_DESC_BAGEL, expectedMessage);
}

@Test
public void parse_twoNameFields_failure() {
String expectedMessage = String.format(MESSAGE_NAME_SPECIFIED_TWICE + "\n" + AddToOrderCommand.MESSAGE_USAGE);

// both name and id prefix missing
assertParseFailure(parser, VALID_NAME_BAGEL + " " + PREFIX_NAME + VALID_NAME_DONUT, expectedMessage);
}

@Test
public void parse_countZero_failure() {
assertParseFailure(parser, VALID_NAME_BAGEL + ID_DESC_BAGEL + INVALID_COUNT_ZERO,
Expand Down
Loading