diff --git a/src/main/java/seedu/agendum/logic/parser/Parser.java b/src/main/java/seedu/agendum/logic/parser/Parser.java index d85210781f1e..c594b0647b23 100644 --- a/src/main/java/seedu/agendum/logic/parser/Parser.java +++ b/src/main/java/seedu/agendum/logic/parser/Parser.java @@ -41,11 +41,15 @@ public class Parser { private static final Pattern UNALIAS_ARGS_FORMAT = Pattern.compile("(?[\\p{Alnum}]+)"); //@@author A0003878Y + private static final Pattern QUOTATION_FORMAT = Pattern.compile("\'([^\']*)\'"); private static final Pattern ADD_SCHEDULE_ARGS_FORMAT = Pattern.compile("(?:.+?(?=(?:(?:(?i)by|from|to)\\s|$)))+?"); private static final String ARGS_FROM = "from"; private static final String ARGS_BY = "by"; private static final String ARGS_TO = "to"; + private static final String FILLER_WORD = "FILLER "; + private static final String SINGLE_QUOTE = "\'"; + private static final String[] TIME_TOKENS = new String[] { ARGS_FROM, ARGS_TO, ARGS_BY }; private CommandLibrary commandLibrary; @@ -145,7 +149,21 @@ public Command parseCommand(String userInput) { * @return the prepared command */ private Command prepareAdd(String args) { + + // Create title and dateTimeMap + StringBuilder titleBuilder = new StringBuilder(); + HashMap> dateTimeMap = new HashMap<>(); + + // Check for quotation in args. If so, they're set as title + Optional quotationCheck = checkForQuotation(args); + if (quotationCheck.isPresent()) { + titleBuilder.append(quotationCheck.get().replace(SINGLE_QUOTE,"")); + args = FILLER_WORD + args.replace(quotationCheck.get(),""); // This will get removed later by regex + } + + // Start parsing for datetime in args Matcher matcher = ADD_SCHEDULE_ARGS_FORMAT.matcher(args.trim()); + if (!matcher.matches()) { return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } @@ -153,8 +171,9 @@ private Command prepareAdd(String args) { try { matcher.reset(); matcher.find(); - StringBuilder titleBuilder = new StringBuilder(matcher.group(0)); - HashMap> dateTimeMap = new HashMap<>(); + if (titleBuilder.length() == 0) { + titleBuilder.append(matcher.group(0)); + } // Run this function on all matched groups BiConsumer consumer = (matchedGroup, token) -> { @@ -165,21 +184,31 @@ private Command prepareAdd(String args) { titleBuilder.append(matchedGroup); } }; - scheduleMatcherOnConsumer(matcher, consumer); + executeOnEveryMatcherToken(matcher, consumer); String title = titleBuilder.toString(); - if (dateTimeMap.containsKey(ARGS_BY)) { + if (title.length() == 0) { + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } + + boolean hasDeadlineKeyword = dateTimeMap.containsKey(ARGS_BY); + boolean hasStartTimeKeyword = dateTimeMap.containsKey(ARGS_FROM); + boolean hasEndTimeKeyword = dateTimeMap.containsKey(ARGS_TO); + + if (hasDeadlineKeyword && !hasStartTimeKeyword && !hasEndTimeKeyword) { return new AddCommand(title, dateTimeMap.get(ARGS_BY)); - } else if (dateTimeMap.containsKey(ARGS_FROM) && dateTimeMap.containsKey(ARGS_TO)) { + } + + if (!hasDeadlineKeyword && hasStartTimeKeyword && hasEndTimeKeyword) { return new AddCommand(title, dateTimeMap.get(ARGS_FROM), dateTimeMap.get(ARGS_TO)); - } else if (!dateTimeMap.containsKey(ARGS_FROM) && !dateTimeMap.containsKey(ARGS_TO) - && !dateTimeMap.containsKey(ARGS_BY)) { + } + + if (!hasDeadlineKeyword && !hasStartTimeKeyword && !hasEndTimeKeyword) { return new AddCommand(title); - } else { - return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, - AddCommand.MESSAGE_USAGE)); } + + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } catch (IllegalValueException ive) { return new IncorrectCommand(ive.getMessage()); } @@ -218,19 +247,38 @@ private Command prepareSchedule(String args) { dateTimeMap.put(token, DateTimeUtils.parseNaturalLanguageDateTimeString(time)); } }; - scheduleMatcherOnConsumer(matcher, consumer); + executeOnEveryMatcherToken(matcher, consumer); - if (dateTimeMap.containsKey(ARGS_BY)) { + boolean hasDeadlineKeyword = dateTimeMap.containsKey(ARGS_BY); + boolean hasStartTimeKeyword = dateTimeMap.containsKey(ARGS_FROM); + boolean hasEndTimeKeyword = dateTimeMap.containsKey(ARGS_TO); + + if (hasDeadlineKeyword && !hasStartTimeKeyword && !hasEndTimeKeyword) { return new ScheduleCommand(index, Optional.empty(), dateTimeMap.get(ARGS_BY)); - } else if (dateTimeMap.containsKey(ARGS_FROM) && dateTimeMap.containsKey(ARGS_TO)) { - return new ScheduleCommand(index, dateTimeMap.get(ARGS_FROM), dateTimeMap.get(ARGS_TO)); - } else if (!dateTimeMap.containsKey(ARGS_FROM) && !dateTimeMap.containsKey(ARGS_TO) - && !dateTimeMap.containsKey(ARGS_BY)) { - return new ScheduleCommand(index, Optional.empty(), Optional.empty()); - } else { - return new IncorrectCommand( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, ScheduleCommand.MESSAGE_USAGE)); } + + if (!hasDeadlineKeyword && hasStartTimeKeyword && hasEndTimeKeyword) { + return new ScheduleCommand(index, dateTimeMap.get(ARGS_FROM), dateTimeMap.get(ARGS_TO));} + + if (!hasDeadlineKeyword && !hasStartTimeKeyword && !hasEndTimeKeyword) { + return new ScheduleCommand(index, Optional.empty(), Optional.empty()); + } + + return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ScheduleCommand.MESSAGE_USAGE)); + } + + /** + * Checks if there are any quotation marks in the given string + * + * @param str + * @return returns the string inside the quote. + */ + private Optional checkForQuotation(String str) { + Matcher matcher = QUOTATION_FORMAT.matcher(str.trim()); + if (!matcher.find()) { + return Optional.empty(); + } + return Optional.of(matcher.group(0)); } /** @@ -240,7 +288,7 @@ private Command prepareSchedule(String args) { * @param matcher matcher for current command context * @param consumer closure to execute on */ - private void scheduleMatcherOnConsumer(Matcher matcher, BiConsumer consumer) { + private void executeOnEveryMatcherToken(Matcher matcher, BiConsumer consumer) { while (matcher.find()) { for (String token : TIME_TOKENS) { String matchedGroup = matcher.group(0).toLowerCase(); diff --git a/src/test/java/seedu/agendum/logic/LogicManagerTest.java b/src/test/java/seedu/agendum/logic/LogicManagerTest.java index 0ed3b28e7086..1cdb757081c7 100644 --- a/src/test/java/seedu/agendum/logic/LogicManagerTest.java +++ b/src/test/java/seedu/agendum/logic/LogicManagerTest.java @@ -201,6 +201,21 @@ public void execute_addEventTask_successful() throws Exception { expectedTDL.getTaskList()); } + @Test + public void execute_addEscapeDateTimeParsing() throws Exception { + TestDataHelper helper = new TestDataHelper(); + Task toBeAdded = helper.generateTaskWithName("drop from 21 to 1"); + ToDoList expectedTDL = new ToDoList(); + expectedTDL.addTask(toBeAdded); + + // execute command and verify result + assertCommandBehavior("add 'drop from 21 to 1'", + String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded), + expectedTDL, + expectedTDL.getTaskList()); + } + + @Test public void executeAddDuplicateNotAllowed() throws Exception { // setup expectations