From 96fd9657d536edd873c25c0749e413acf86ee901 Mon Sep 17 00:00:00 2001 From: marcus_pzj Date: Thu, 28 Feb 2019 15:22:37 +0800 Subject: [PATCH] History Command 68d09acd4594b1b0b98545730656c93a05012400 author marcus_pzj 1551338557 +0800 committer marcus_pzj 1551344649 +0800 parent 68d09acd4594b1b0b98545730656c93a05012400 author marcus_pzj 1551338557 +0800 committer marcus_pzj 1551344644 +0800 Add CommandHistory and History Command DeveloperGuide: Minor changes to DeveloperGuide DeveloperGuide: Minor updates Add: Skeleton of Add functionality (#25) * Add skeleton code for add slots * UserGuide.adoc: update add command * fixed checkstyle errors * fix checkstyle errors * fix tests fails due to string format errors caused by refactoring mistake * Utils.java: remove unused methods Planner: Initialize planner according to academic semester Add Javadoc comment for History Command Removed trailing whitespace Update User Docs DeveloperGuide: Minor changes to DeveloperGuide DeveloperGuide: Minor updates Planner: Initialize planner according to academic semester Removed trailing whitespace Update User Docs --- AcademicCalendar.txt | 52 +++++++ PlanMySem.txt | 7 + docs/DeveloperGuide.adoc | 4 +- docs/UserGuide.adoc | 7 +- src/planmysem/commands/AddCommand.java | 2 +- src/planmysem/commands/AddCommandP.java | 88 ++++++++--- src/planmysem/commands/CommandResultP.java | 2 +- src/planmysem/commands/ExitCommandP.java | 19 +++ src/planmysem/commands/HistoryCommand.java | 37 +++++ src/planmysem/common/Utils.java | 107 +++++++++++++- src/planmysem/data/Planner.java | 117 +++++++++++++-- src/planmysem/data/recurrence/Recurrence.java | 85 +++++++++++ src/planmysem/data/semester/Day.java | 4 + src/planmysem/data/semester/ReadOnlyDay.java | 1 - .../data/semester/ReadOnlySemester.java | 4 +- src/planmysem/data/semester/Semester.java | 41 ++++-- src/planmysem/data/slot/Location.java | 3 +- src/planmysem/data/slot/Slot.java | 15 +- src/planmysem/parser/CommandHistory.java | 58 ++++++++ src/planmysem/parser/Parser.java | 7 +- src/planmysem/parser/ParserP.java | 138 +++++++++++++++--- src/planmysem/storage/StorageFileP.java | 2 +- .../storage/jaxb/AdaptedSemester.java | 21 ++- test/java/planmysem/logic/LogicTest.java | 66 ++++----- test/java/planmysem/parser/ParserTest.java | 90 ++++++------ 25 files changed, 810 insertions(+), 167 deletions(-) create mode 100644 AcademicCalendar.txt create mode 100644 PlanMySem.txt create mode 100644 src/planmysem/commands/ExitCommandP.java create mode 100644 src/planmysem/commands/HistoryCommand.java create mode 100644 src/planmysem/data/recurrence/Recurrence.java create mode 100644 src/planmysem/parser/CommandHistory.java diff --git a/AcademicCalendar.txt b/AcademicCalendar.txt new file mode 100644 index 000000000..40bd2316d --- /dev/null +++ b/AcademicCalendar.txt @@ -0,0 +1,52 @@ +1:Vacation +2:Vacation +3:Week 1 +4:Week 2 +5:Week 3 +6:Week 4 +7:Week 5 +8:Week 6 +9:Recess Week +10:Week 7 +11:Week 8 +12:Week 9 +13:Week 10 +14:Week 11 +15:Week 12 +16:Week 13 +17:Reading Week +18:Examination Week +19:Examination Week +20:Vacation +21:Vacation +22:Vacation +23:Vacation +24:Vacation +25:Vacation +26:Vacation +27:Vacation +28:Vacation +29:Vacation +30:Vacation +31:Vacation +32:Orientation Week +33:Week 1 +34:Week 2 +35:Week 3 +36:Week 4 +37:Week 5 +38:Week 6 +39:Recess Week +40:Week 7 +41:Week 8 +42:Week 9 +43:Week 10 +44:Week 11 +45:Week 12 +46:Week 13 +47:Reading Week +48:Examination Week +49:Examination Week +50:Vacation +51:Vacation +52:Vacation \ No newline at end of file diff --git a/PlanMySem.txt b/PlanMySem.txt new file mode 100644 index 000000000..fdd022c1d --- /dev/null +++ b/PlanMySem.txt @@ -0,0 +1,7 @@ + + + + 0 + + + diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index aef34f485..2220a1829 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -13,9 +13,9 @@ ifdef::env-github[] :warning-caption: :warning: :experimental: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level4/tree/master +:repoURL: https://github.com/CS2113-AY1819S2-T08-3/main -By: `T08-3` +By: `T08-3` Since: `Jan 2019` Licence: `MIT` == Setting up diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 1d8d86288..690249735 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -85,7 +85,8 @@ Format: `help` === Adding Slots : `add` Add a _slot_ to the planner. + -Format: `add slot d/DATE t/TIME d/DURATION [r/norecess] [r/noreading] [r/nonormal] [r/recess] [r/reading] [r/normal] [tag/TAG]...` +Format: `add NAME st/START_TIME et/END_TIME|DURATION [date/DATE|DAY_OF_WEEK] + +[l/LOCATION] [d/DESCRIPTION] [r/recess|norecess] [r/reading|noreading] [r/normal|nonormal] [t/TAG]...` Examples: @@ -183,12 +184,12 @@ all the details in the planner. === Listing previous inputted commands : `history` -View the history of commands previously inputted. + +Lists all the commands that you have entered in reverse chronological order. + Format: history === Undoing previous command : `undo` -Restories the planner to the state before the previous command was executed. + +Restores the planner to the state before the previous command was executed. + Format: `undo` [NOTE] diff --git a/src/planmysem/commands/AddCommand.java b/src/planmysem/commands/AddCommand.java index 69d9acecd..e040f7961 100644 --- a/src/planmysem/commands/AddCommand.java +++ b/src/planmysem/commands/AddCommand.java @@ -18,7 +18,7 @@ */ public class AddCommand extends Command { - public static final String COMMAND_WORD = "addDay"; + public static final String COMMAND_WORD = "add"; public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Adds a person to the address book. " + "Contact details can be marked private by prepending 'p' to the prefix.\n\t" diff --git a/src/planmysem/commands/AddCommandP.java b/src/planmysem/commands/AddCommandP.java index c5d55d063..1a21c7967 100644 --- a/src/planmysem/commands/AddCommandP.java +++ b/src/planmysem/commands/AddCommandP.java @@ -3,8 +3,11 @@ import java.util.HashSet; import java.util.Set; +import javafx.util.Pair; import planmysem.common.Utils; import planmysem.data.exception.IllegalValueException; +import planmysem.data.recurrence.Recurrence; +import planmysem.data.semester.Semester; import planmysem.data.slot.Description; import planmysem.data.slot.Location; import planmysem.data.slot.Name; @@ -19,49 +22,90 @@ public class AddCommandP extends CommandP { public static final String COMMAND_WORD = "add"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Adds a slot to the Planner. " - + "Contact details can be marked private by prepending 'p' to the prefix.\n\t" - + "Parameters: NAME [p]p/PHONE [p]e/EMAIL [p]a/ADDRESS [t/TAG]...\n\t" - + "Example: " + COMMAND_WORD - + " John Doe p/98765432 e/johnd@gmail.com a/311, Clementi Ave 2, #02-25 t/friends t/owesMoney"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Adds a single or multiple slot to the Planner." + + "\n\tParameters: NAME st/START_TIME et/END_TIME|DURATION [date/DATE|DAY_OF_WEEK] " + + "[l/LOCATION] [d/DESCRIPTION] [r/recess|norecess] [r/reading|noreading] [r/normal|nonormal] [t/TAG].." + + "\n\tExample: " + COMMAND_WORD + + " CS2113T Tutorial st/0800 et/0900 date/tuesday l/COM2 r/norecess r/noreading " + + " t/tutorial t/cs2113t t/module"; - public static final String MESSAGE_SUCCESS = "New slot added: %1$s"; - // public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; + public static final String MESSAGE_SUCCESS = "New slot(s) added: %1$s"; + public static final String MESSAGE_SUCCESS_NO_CHANGE = "No slots were added."; + public static final String MESSAGE_FAIL_OUT_OF_BOUNCE = "Date specified is out of bounce."; - private final Slot slot; + private final Pair toAdd; /** * Convenience constructor using raw values. * * @throws IllegalValueException if any of the raw values are invalid */ - public AddCommandP(String name, String location, String description, String startTime, - int duration, Set tags) throws IllegalValueException { + public AddCommandP(String date, String name, String location, String description, String startTime, + String endTime, Set tags, Set recurrences) throws IllegalValueException { + + final Set tagSet = new HashSet<>(); + for (String tagName : tags) { + tagSet.add(new Tag(tagName)); + } + + // check variable date if it is a date or day + int day = Utils.getDay(date); + if (day != 0) { + toAdd = new Pair<>(new Slot(new Name(name), new Location(location), new Description(description), + Utils.parseTime(startTime), Utils.parseTime(endTime), tagSet), new Recurrence(recurrences, day)); + } else { + toAdd = new Pair<>(new Slot(new Name(name), new Location(location), new Description(description), + Utils.parseTime(startTime), Utils.parseTime(endTime), tagSet), + new Recurrence(recurrences, Utils.parseDate(date))); + } + } + + /** + * Convenience constructor using raw values. + * + * @throws IllegalValueException if any of the raw values are invalid + */ + public AddCommandP(String date, String name, String location, String description, String startTime, + int duration, Set tags, Set recurrences) throws IllegalValueException { final Set tagSet = new HashSet<>(); for (String tagName : tags) { tagSet.add(new Tag(tagName)); } - this.slot = new Slot(new Name(name), new Location(location), new Description(description), - Utils.getLocalTime(startTime), duration, tagSet); + // check variable date if it is a date or day + int day = Utils.getDay(date); + if (day != 0) { + toAdd = new Pair<>(new Slot(new Name(name), new Location(location), new Description(description), + Utils.parseTime(startTime), duration, tagSet), new Recurrence(recurrences, day)); + } else { + toAdd = new Pair<>(new Slot(new Name(name), new Location(location), new Description(description), + Utils.parseTime(startTime), duration, tagSet), new Recurrence(recurrences, Utils.parseDate(date))); + } } - public AddCommandP(Slot slot) { - this.slot = slot; + public AddCommandP(Pair toAdd) { + this.toAdd = toAdd; } - public ReadOnlySlot getSlot() { - return slot; + + public Pair getSlots() { + return toAdd; } @Override public CommandResultP execute() { - // try { - planner.addSlot(slot); - return new CommandResultP(String.format(MESSAGE_SUCCESS, slot)); - // } catch (UniquePersonList.DuplicatePersonException dpe) { - // return new CommandResult(MESSAGE_DUPLICATE_PERSON); - // } + try { + int addCount = planner.addSlots(toAdd); + + if (addCount == 1) { + return new CommandResultP(MESSAGE_SUCCESS_NO_CHANGE); + } else { + return new CommandResultP(String.format(MESSAGE_SUCCESS, toAdd)); + } + } catch (Semester.DayNotFoundException dnfe) { + return new CommandResultP(MESSAGE_FAIL_OUT_OF_BOUNCE); + } + } } diff --git a/src/planmysem/commands/CommandResultP.java b/src/planmysem/commands/CommandResultP.java index fdcc421c7..6f01f3b5f 100644 --- a/src/planmysem/commands/CommandResultP.java +++ b/src/planmysem/commands/CommandResultP.java @@ -17,7 +17,7 @@ public class CommandResultP { public final String feedbackToUser; /** - * The list of persons that was produced by the command + * The list of days that was produced by the command */ private final HashMap days; diff --git a/src/planmysem/commands/ExitCommandP.java b/src/planmysem/commands/ExitCommandP.java new file mode 100644 index 000000000..3dcba5263 --- /dev/null +++ b/src/planmysem/commands/ExitCommandP.java @@ -0,0 +1,19 @@ +package planmysem.commands; + +/** + * Terminates the program. + */ +public class ExitCommandP extends CommandP { + + public static final String COMMAND_WORD = "exit"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Exits the program.\n\t" + + "Example: " + COMMAND_WORD; + public static final String MESSAGE_EXIT_ACKNOWEDGEMENT = "Exiting PlanMySem as requested ..."; + + @Override + public CommandResultP execute() { + return new CommandResultP(MESSAGE_EXIT_ACKNOWEDGEMENT); + } + +} diff --git a/src/planmysem/commands/HistoryCommand.java b/src/planmysem/commands/HistoryCommand.java new file mode 100644 index 000000000..bd9e41e8e --- /dev/null +++ b/src/planmysem/commands/HistoryCommand.java @@ -0,0 +1,37 @@ +package planmysem.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.Collections; + +import planmysem.parser.CommandHistory; + +/** + * Lists all the commands entered by user from the start of app launch. + */ +public class HistoryCommand extends CommandP { + + public static final String COMMAND_WORD = "history"; + public static final String MESSAGE_SUCCESS = "Entered commands (from most recent to earliest):\n%1$s"; + public static final String MESSAGE_NO_HISTORY = "You have not yet entered any commands."; + + /** + * + * @param history + * @return CommandResultP with a list of previousCommands + */ + public CommandResultP execute(CommandHistory history) { + requireNonNull(history); + ArrayList previousCommands = new ArrayList<>(history.getHistory()); + + if (previousCommands.isEmpty()) { + return new CommandResultP(MESSAGE_NO_HISTORY); + } + + Collections.reverse(previousCommands); + return new CommandResultP(String.format(MESSAGE_SUCCESS, String.join("\n", previousCommands))); + } + +} + diff --git a/src/planmysem/common/Utils.java b/src/planmysem/common/Utils.java index 40200b584..e67d50269 100644 --- a/src/planmysem/common/Utils.java +++ b/src/planmysem/common/Utils.java @@ -1,14 +1,27 @@ package planmysem.common; +import static java.time.temporal.ChronoUnit.MINUTES; + +import java.time.LocalDate; import java.time.LocalTime; +import java.time.format.DateTimeFormatter; import java.util.Collection; import java.util.HashSet; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Utility methods */ public class Utils { + public static final Pattern DATE_FORMAT = + Pattern.compile("(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-((19|20)\\d\\d)"); + + public static final Pattern TWELVE_HOUR_FORMAT = + Pattern.compile("(1[012]|[1-9]):[0-5][0-9](\\s)?(?i)(am|pm)"); + public static final Pattern TWENTY_FOUR_HOUR_FORMAT = + Pattern.compile("([01]?[0-9]|2[0-3]):[0-5][0-9]\n"); /** * Checks whether any of the given items are null. @@ -37,9 +50,97 @@ public static boolean elementsAreUnique(Collection items) { } /** - * TODO + * Check if String represents an actual date or day. + * Returns 0 if string does not represent a DayOfWeek, else returns int corresponding to the day. + */ + public static int getDay(String unknown) { + String day = unknown.toLowerCase(); + + int result = 0; + switch (day) { + case "monday": + case "mon": + case "1": + result = 1; + break; + + case "tuesday": + case "tues": + case "2": + result = 2; + break; + + case "wednesday": + case "wed": + case "3": + result = 3; + break; + case "thursday": + case "thurs": + case "4": + result = 4; + break; + + case "friday": + case "fri": + case "5": + result = 5; + break; + + default: + result = 0; + } + + return result; + } + + /** + * Parse String LocalDate. + */ + public static LocalDate parseDate(String date) { + LocalDate localDate = null; + Matcher dateMatcher = DATE_FORMAT.matcher(date); + + if (dateMatcher.matches()) { + localDate = LocalDate.parse(date, DateTimeFormatter.ofPattern("d-MM-yyyy")); + } + + return localDate; + } + + /** + * Parse String to 12 hour or 24 hour time format. + */ + public static LocalTime parseTime(String time) { + LocalTime localTime = null; + Matcher twelveHourMatcher = TWELVE_HOUR_FORMAT.matcher(time); + Matcher twentyFourHourMatcher = TWENTY_FOUR_HOUR_FORMAT.matcher(time); + + // Parse start time into 12 hour format, else, as 24 Hour Format + if (twelveHourMatcher.matches()) { + localTime = LocalTime.parse(time, DateTimeFormatter.ofPattern("hh:mm:ss a")); + } else if (twentyFourHourMatcher.matches()) { + localTime = LocalTime.parse(time, DateTimeFormatter.ofPattern("kk:mm:ss")); + } + + return localTime; + } + + /** + * Get the time difference between two LocalTimes + */ + public static int getDuration(LocalTime startTime, LocalTime endTime) { + return (int) MINUTES.between(startTime, endTime); + } + + /** + * Get number of matches */ - public static LocalTime getLocalTime(String date) { - return LocalTime.of(1, 1, 1); + public static int countMatches(Matcher matcher) { + int counter = 0; + while (matcher.find()) { + counter++; + } + return counter; } } diff --git a/src/planmysem/data/Planner.java b/src/planmysem/data/Planner.java index 02c5a864a..09e675441 100644 --- a/src/planmysem/data/Planner.java +++ b/src/planmysem/data/Planner.java @@ -1,14 +1,25 @@ package planmysem.data; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.time.LocalDate; - +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javafx.util.Pair; +import planmysem.data.recurrence.Recurrence; import planmysem.data.semester.Day; import planmysem.data.semester.ReadOnlyDay; import planmysem.data.semester.Semester; import planmysem.data.slot.Slot; /** - * Represents the entire address book. Contains the data of the address book. + * Represents the entire Planner. Contains the data of the Planner. */ public class Planner { private final Semester semester; @@ -17,12 +28,89 @@ public class Planner { * Creates an empty planner. */ public Planner() { - semester = new Semester(); - - // TODO: initialize semester with data of days & determine values of months, weeks, recess week and reading week - // Dummy data - // HashMap days = new HashMap<>(); - // days.put(LocalDate.now(), new Day(DayOfWeek.MONDAY)); + String filePath = "AcademicCalendar.txt"; + String acadWeek = null; + String acadYear = null; + String acadSem = null; + int noOfWeeks = 0; + Calendar cal = Calendar.getInstance(); + int currentWeekOfYear = cal.get(Calendar.WEEK_OF_YEAR); + int currentYear = cal.get(Calendar.YEAR); + LocalDate startDate = LocalDate.now(); + LocalDate endDate = LocalDate.now(); + // Read AcademicCalendar.txt to get current academic week + try { + Stream lines = Files.lines(Paths.get(filePath)); + Map acadCalMap = lines + .collect(Collectors.toMap(key -> key.split(":")[0], val -> val.split(":")[1])); + acadWeek = acadCalMap.get(Integer.toString(currentWeekOfYear)); + } catch (IOException ioe) { + ioe.getMessage(); + } + + // Set variables if it is currently vacation + if (acadWeek != null && acadWeek.equals("Vacation")) { + acadSem = "Vacation"; + if (currentWeekOfYear < 3 || currentWeekOfYear > 49) { + noOfWeeks = 5; + + cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); + cal.set(Calendar.WEEK_OF_YEAR, 50); + startDate = LocalDate.of(currentYear, 12, cal.get(Calendar.DATE)); + + cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); + cal.set(Calendar.WEEK_OF_YEAR, 2); + endDate = LocalDate.of(currentYear, 1, cal.get(Calendar.DATE)); + } + if (currentWeekOfYear > 19 && currentWeekOfYear < 32) { + noOfWeeks = 12; + + cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); + cal.set(Calendar.WEEK_OF_YEAR, 20); + startDate = LocalDate.of(currentYear, 5, cal.get(Calendar.DATE)); + + cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); + cal.set(Calendar.WEEK_OF_YEAR, 31); + endDate = LocalDate.of(currentYear, 8, cal.get(Calendar.DATE)); + } + } + + // Set variables if it is currently not vacation + if (currentWeekOfYear > 31 && currentWeekOfYear < 50) { + acadYear = "AY" + currentYear + "/" + (currentYear + 1); + acadSem = "Sem 1"; + noOfWeeks = 18; + + cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); + cal.set(Calendar.WEEK_OF_YEAR, 32); + startDate = LocalDate.of(currentYear, 8, cal.get(Calendar.DATE)); + + cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY); + cal.set(Calendar.WEEK_OF_YEAR, 49); + endDate = LocalDate.of(currentYear, 12, cal.get(Calendar.DATE)); + } + if (currentWeekOfYear > 2 && currentWeekOfYear < 20) { + acadYear = "AY" + (currentYear - 1) + "/" + currentYear; + acadSem = "Sem 2"; + noOfWeeks = 17; + + cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); + cal.set(Calendar.WEEK_OF_YEAR, 3); + startDate = LocalDate.of(currentYear, 1, cal.get(Calendar.DATE)); + + cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY); + cal.set(Calendar.WEEK_OF_YEAR, 19); + endDate = LocalDate.of(currentYear, 5, cal.get(Calendar.DATE)); + } + + // Initialise hashmap of all days in current semester + HashMap days = new HashMap<>(); + List datesList = startDate.datesUntil(endDate).collect(Collectors.toList()); + for (LocalDate date: datesList) { + days.put(date, new Day(date.getDayOfWeek())); + } + semester = new Semester(acadSem, acadYear, days, startDate, endDate, noOfWeeks); + // TODO: set constants for fixed numbers, simplify/optimise code, handle ioe exception } /** @@ -51,8 +139,15 @@ public void addDay(LocalDate date, Day day) throws Semester.DuplicateDayExceptio * Adds a slot to the Planner. * */ - public void addSlot(Slot slot) { - semester.addSlot(slot); + public void addSlot(LocalDate date, Slot slot) { + semester.addSlot(date, slot); + } + + /** + * Adds slots to the Planner. + */ + public int addSlots(Pair slots) throws Semester.DayNotFoundException { + return semester.addSlots(slots); } /** @@ -105,7 +200,7 @@ public void clearSlots() { * Defensively copy the Semester in the Planner at the time of the call. */ public Semester getSemester() { - return new Semester(semester); + return semester; } @Override diff --git a/src/planmysem/data/recurrence/Recurrence.java b/src/planmysem/data/recurrence/Recurrence.java new file mode 100644 index 000000000..469310b1a --- /dev/null +++ b/src/planmysem/data/recurrence/Recurrence.java @@ -0,0 +1,85 @@ +package planmysem.data.recurrence; + +import java.time.LocalDate; +import java.util.Set; + +/** + * Represents a Recurrence Value of a slot in the Planner. + */ +public class Recurrence { + public final boolean recess; // Represents recess week + public final boolean reading; // Represents reading week + public final boolean normal; // Represents normal academic weeks + public final int day; + public final LocalDate date; + + /** + * Generate Recurrence values from a set and recurse over a given day. + */ + public Recurrence(Set recurrences, int day) { + if (recurrences.contains("recess")) { + recess = true; + } else { + recess = false; + } + if (recurrences.contains("reading")) { + reading = true; + } else { + reading = false; + } + if (recurrences.contains("normal")) { + normal = true; + } else { + normal = false; + } + this.day = day; + date = null; + } + + /** + * Generate Recurrence values from a set and recurse over a given date. + */ + public Recurrence(Set recurrences, LocalDate date) { + if (recurrences.contains("recess")) { + recess = true; + } else { + recess = false; + } + if (recurrences.contains("reading")) { + reading = true; + } else { + reading = false; + } + if (recurrences.contains("normal")) { + normal = true; + } else { + normal = false; + } + this.day = 0; + this.date = date; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Recurrence // instanceof handles nulls + && this.recess == ((Recurrence) other).recess + && this.reading == ((Recurrence) other).reading + && this.normal == ((Recurrence) other).normal); // state check + } + + @Override + public int hashCode() { + int hashCode = 0; + if (recess) { + hashCode += 1; // 0001 + } + if (reading) { + hashCode += 2; // 0010 + } + if (normal) { + hashCode += 4; // 0100 + } + return hashCode; + } +} diff --git a/src/planmysem/data/semester/Day.java b/src/planmysem/data/semester/Day.java index 220e99411..6d8033a49 100644 --- a/src/planmysem/data/semester/Day.java +++ b/src/planmysem/data/semester/Day.java @@ -25,6 +25,10 @@ public Day(DayOfWeek dayOfWeek, ArrayList slots) { this.slots = slots; } + public void setSlot(Slot slot) { + slots.add(slot); + } + public void clear() { slots.clear(); } diff --git a/src/planmysem/data/semester/ReadOnlyDay.java b/src/planmysem/data/semester/ReadOnlyDay.java index 5ea560be0..b1ee710ee 100644 --- a/src/planmysem/data/semester/ReadOnlyDay.java +++ b/src/planmysem/data/semester/ReadOnlyDay.java @@ -15,5 +15,4 @@ public interface ReadOnlyDay { public String getDay(); public ArrayList getSlots(); - } diff --git a/src/planmysem/data/semester/ReadOnlySemester.java b/src/planmysem/data/semester/ReadOnlySemester.java index dd46cad3a..97b772bcb 100644 --- a/src/planmysem/data/semester/ReadOnlySemester.java +++ b/src/planmysem/data/semester/ReadOnlySemester.java @@ -11,7 +11,7 @@ public interface ReadOnlySemester { String getName(); String getAcademicYear(); HashMap getDays(); - String getStartDate(); - String getEndDate(); + LocalDate getStartDate(); + LocalDate getEndDate(); int getNoOfWeeks(); } diff --git a/src/planmysem/data/semester/Semester.java b/src/planmysem/data/semester/Semester.java index 7de54d50e..f82e7ac06 100644 --- a/src/planmysem/data/semester/Semester.java +++ b/src/planmysem/data/semester/Semester.java @@ -4,7 +4,9 @@ import java.util.HashMap; import java.util.Map; +import javafx.util.Pair; import planmysem.data.exception.DuplicateDataException; +import planmysem.data.recurrence.Recurrence; import planmysem.data.slot.Slot; /** @@ -16,8 +18,8 @@ public class Semester implements ReadOnlySemester { private final String name; private final String academicYear; private final HashMap days = new HashMap<>(); - private final String startDate; - private final String endDate; + private final LocalDate startDate; + private final LocalDate endDate; private final int noOfWeeks; /** @@ -34,8 +36,8 @@ public Semester() { /** * Constructs a semester with the given Days. */ - public Semester(String name, String academicYear, HashMap days, String startDate, - String endDate, int noOfWeeks) { + public Semester(String name, String academicYear, HashMap days, LocalDate startDate, + LocalDate endDate, int noOfWeeks) { this.name = name; this.academicYear = academicYear; this.days.putAll(days); @@ -57,7 +59,7 @@ public Semester(Semester source) { } /** - * Adds a day to the list. + * Adds a Day to the list. * * @throws DuplicateDayException if the Day to addDay is a duplicate of an existing Day in the list. */ @@ -69,13 +71,31 @@ public void addDay(LocalDate date, Day day) throws DuplicateDayException { } /** - * Adds a person to the list. + * Adds a Slot to the Semester. * */ - public void addSlot(Slot slot) { - // days.put(date, day); + public void addSlot(LocalDate date, Slot slot) { + days.get(date).setSlot(slot); } + /** + * Adds Slots to the Semester. + * + */ + public int addSlots(Pair slots) throws DayNotFoundException { + int result = 0; + + if (slots.getValue().date.isBefore(startDate) || slots.getValue().date.isAfter(endDate)) { + throw new DayNotFoundException(); + } + + // Generate dates to add + // Perform add + + return result; + } + + /** * Removes the equivalent Day from the list. * @@ -147,12 +167,12 @@ public HashMap getDays() { } @Override - public String getStartDate() { + public LocalDate getStartDate() { return startDate; } @Override - public String getEndDate() { + public LocalDate getEndDate() { return endDate; } @@ -176,4 +196,5 @@ protected DuplicateDayException() { */ public static class DayNotFoundException extends Exception { } + } diff --git a/src/planmysem/data/slot/Location.java b/src/planmysem/data/slot/Location.java index 343314df9..1b5f09968 100644 --- a/src/planmysem/data/slot/Location.java +++ b/src/planmysem/data/slot/Location.java @@ -8,7 +8,8 @@ */ public class Location { public static final String EXAMPLE = "NUS COM2 04-22"; - public static final String MESSAGE_NAME_CONSTRAINTS = "Slot's location should be spaces or alphanumeric characters"; + public static final String MESSAGE_NAME_CONSTRAINTS = + "Slot's location should be spaces or alphanumeric characters"; public static final String NAME_VALIDATION_REGEX = "[\\p{Alnum} ]+"; private String value; diff --git a/src/planmysem/data/slot/Slot.java b/src/planmysem/data/slot/Slot.java index f0e018fd1..f69e1ed45 100644 --- a/src/planmysem/data/slot/Slot.java +++ b/src/planmysem/data/slot/Slot.java @@ -5,6 +5,7 @@ import java.util.Objects; import java.util.Set; +import planmysem.common.Utils; import planmysem.data.tag.Tag; /** @@ -19,6 +20,19 @@ public class Slot implements ReadOnlySlot { private LocalTime startTime; private int duration; + /** + * Assumption: Every field must be present and not null. + */ + public Slot(Name name, Location location, Description description, + LocalTime startTime, LocalTime endTime, Set tags) { + this.name = name; + this.location = location; + this.description = description; + this.startTime = startTime; + this.duration = Utils.getDuration(startTime, endTime); + this.tags.addAll(tags); + } + /** * Assumption: Every field must be present and not null. */ @@ -60,7 +74,6 @@ public int getDuration() { return duration; } - @Override public LocalTime getTime() { return startTime; diff --git a/src/planmysem/parser/CommandHistory.java b/src/planmysem/parser/CommandHistory.java new file mode 100644 index 000000000..a2098f3d8 --- /dev/null +++ b/src/planmysem/parser/CommandHistory.java @@ -0,0 +1,58 @@ +package planmysem.parser; + +import static java.util.Objects.requireNonNull; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +/** + * Stores the history of commands executed. + */ +public class CommandHistory { + private final ObservableList userInputHistory = FXCollections.observableArrayList(); + private final ObservableList unmodifiableUserInputHistory = + FXCollections.unmodifiableObservableList(userInputHistory); + + public CommandHistory() {} + + public CommandHistory(CommandHistory commandHistory) { + userInputHistory.addAll(commandHistory.userInputHistory); + } + /** + * Appends {@code userInput} to the list of user input entered. + */ + public void add(String userInput) { + requireNonNull(userInput); + userInputHistory.add(userInput); + } + + /** + * Returns an unmodifiable view of {@code userInputHistory}. + */ + public ObservableList getHistory() { + return unmodifiableUserInputHistory; + } + + @Override + public boolean equals(Object obj) { + // short circuit if same object + if (obj == this) { + return true; + } + + // instanceof handles nulls + if (!(obj instanceof CommandHistory)) { + return false; + } + + // state check + CommandHistory other = (CommandHistory) obj; + return userInputHistory.equals(other.userInputHistory); + } + + @Override + public int hashCode() { + return userInputHistory.hashCode(); + } +} + diff --git a/src/planmysem/parser/Parser.java b/src/planmysem/parser/Parser.java index cf43f57d5..5d371310d 100644 --- a/src/planmysem/parser/Parser.java +++ b/src/planmysem/parser/Parser.java @@ -45,7 +45,8 @@ public class Parser { public static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); /** - * Checks whether the private prefix of a contact detail in the addDay command's arguments string is present. + * Checks whether the private prefix of a contact detail in the + * add command's arguments string is present. */ private static boolean isPrivatePrefixPresent(String matchedPrefix) { return matchedPrefix.equals("p"); @@ -61,7 +62,9 @@ private static Set getTagsFromArgs(String tagArguments) throws IllegalVa return Collections.emptySet(); } // replace first delimiter prefix, then split - final Collection tagStrings = Arrays.asList(tagArguments.replaceFirst(" t/", "").split(" t/")); + final Collection tagStrings = + Arrays.asList(tagArguments.replaceFirst( + " t/", "").split(" t/")); return new HashSet<>(tagStrings); } diff --git a/src/planmysem/parser/ParserP.java b/src/planmysem/parser/ParserP.java index 35f07b188..8898b76ec 100644 --- a/src/planmysem/parser/ParserP.java +++ b/src/planmysem/parser/ParserP.java @@ -10,10 +10,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import planmysem.commands.AddCommand; +import planmysem.commands.AddCommandP; import planmysem.commands.Command; import planmysem.commands.CommandP; import planmysem.commands.DeleteCommand; +import planmysem.commands.ExitCommandP; import planmysem.commands.FindCommand; import planmysem.commands.HelpCommand; import planmysem.commands.HelpCommandP; @@ -33,35 +34,58 @@ public class ParserP { public static final Pattern KEYWORDS_ARGS_FORMAT = Pattern.compile("(?\\S+(?:\\s+\\S+)*)"); // one or more keywords separated by whitespace - public static final Pattern PERSON_DATA_ARGS_FORMAT = // '/' forward slashes are reserved for delimiter prefixes - Pattern.compile("(?[^/]+)" - + " (?p?)p/(?[^/]+)" - + " (?p?)e/(?[^/]+)" - + " (?p?)a/(?
[^/]+)" - + "(?(?: t/[^/]+)*)"); // variable number of tags + public static final Pattern SLOT_DATA_ARGS_FORMAT = // '/' forward slashes are reserved for delimiter prefixes + Pattern.compile("n/(?[^/]+)" + + " l/(?[^/]+)" + + " d/(?[^/]+)" + + " st/(?[^/]+)" + + " et/(?[^/]+)" + + " duration/(?[^/]+)" + + "(?(?: t/[^/]+)*)"); // variable number of tags + + public static final Pattern SLOT_NAME_ARGS_FORMAT = Pattern.compile("(?[^/]+)"); + public static final Pattern SLOT_LOCATION_ARGS_FORMAT = Pattern.compile(" l/(?[^/]+)"); + public static final Pattern SLOT_DESCRIPTION_ARGS_FORMAT = Pattern.compile(" d/(?[^/]+)"); + public static final Pattern SLOT_DATE_ARGS_FORMAT = Pattern.compile(" date/(?[^/]+)"); + public static final Pattern SLOT_START_TIME_ARGS_FORMAT = Pattern.compile(" st/(?[^/]+)"); + public static final Pattern SLOT_END_TIME_ARGS_FORMAT = Pattern.compile(" et/(?[^/]+)"); + public static final Pattern SLOT_DURATION_ARGS_FORMAT = Pattern.compile(" et/(?[0-9]+)"); + public static final Pattern SLOT_TAG_ARGS_FORMAT = Pattern.compile("(?(?: t/[^/]+)*)"); + public static final Pattern SLOT_RECURRENCE_ARGS_FORMAT = Pattern.compile("(?(?: r/[^/]+)*)"); + /** * Used for initial separation of command word and args. */ public static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); /** - * Checks whether the private prefix of a contact detail in the addDay command's arguments string is present. + * Extracts the new Slot's tags from the add command's tag arguments string. + * Merges duplicate tag strings. */ - private static boolean isPrivatePrefixPresent(String matchedPrefix) { - return matchedPrefix.equals("p"); + private static Set getTagsFromArgs(String tagArguments) throws IllegalValueException { + // no tags + if (tagArguments.isEmpty()) { + return Collections.emptySet(); + } + // replace first delimiter prefix, then split + final Collection tagStrings = + Arrays.asList( + tagArguments.replaceFirst(" t/", "") + .split(" t/")); + return new HashSet<>(tagStrings); } /** - * Extracts the new person's tags from the addDay command's tag arguments string. - * Merges duplicate tag strings. + * Extracts the new Slot's recursive arguments from the add command's recurse arguments string. + * Merges duplicate recursive strings. */ - private static Set getTagsFromArgs(String tagArguments) throws IllegalValueException { + private static Set getRecurrencesFromArgs(String recursiveArguments) throws IllegalValueException { // no tags - if (tagArguments.isEmpty()) { + if (recursiveArguments.isEmpty()) { return Collections.emptySet(); } // replace first delimiter prefix, then split - final Collection tagStrings = Arrays.asList(tagArguments.replaceFirst(" t/", "").split(" t/")); + final Collection tagStrings = Arrays.asList(recursiveArguments.replaceFirst(" r/", "").split(" r/")); return new HashSet<>(tagStrings); } @@ -77,23 +101,95 @@ public CommandP parseCommand(String userInput) { return new IncorrectCommandP(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); } - return new HelpCommandP(); + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + switch (commandWord) { + + case AddCommandP.COMMAND_WORD: + return prepareAdd(arguments); + + case ExitCommandP.COMMAND_WORD: + return new ExitCommandP(); + + case HelpCommandP.COMMAND_WORD: // Fallthrough + default: + return new HelpCommandP(); + } } /** - * Parses arguments in the context of the addDay person command. + * Parses arguments in the context of the add Slot command. * * @param args full command args string * @return the prepared command */ private CommandP prepareAdd(String args) { - final Matcher matcher = PERSON_DATA_ARGS_FORMAT.matcher(args.trim()); + String arguments = args.trim(); + final Matcher nameMatcher = SLOT_NAME_ARGS_FORMAT.matcher(arguments); + final Matcher locationMatcher = SLOT_LOCATION_ARGS_FORMAT.matcher(arguments); + final Matcher descriptionMatcher = SLOT_DESCRIPTION_ARGS_FORMAT.matcher(arguments); + final Matcher dateMatcher = SLOT_DATE_ARGS_FORMAT.matcher(arguments); + final Matcher startTimeMatcher = SLOT_START_TIME_ARGS_FORMAT.matcher(arguments); + final Matcher endTimeMatcher = SLOT_END_TIME_ARGS_FORMAT.matcher(arguments); + final Matcher durationMatcher = SLOT_DURATION_ARGS_FORMAT.matcher(arguments); + final Matcher tagsMatcher = SLOT_TAG_ARGS_FORMAT.matcher(arguments); + final Matcher recurseMatcher = SLOT_RECURRENCE_ARGS_FORMAT.matcher(arguments); + // Validate arg string format - if (!matcher.matches()) { - return new IncorrectCommandP(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + if (nameMatcher.find() && startTimeMatcher.find() && (endTimeMatcher.find() || durationMatcher.find())) { + return new IncorrectCommandP(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommandP.MESSAGE_USAGE)); } - return new HelpCommandP(); + // Here it does not matter if it is a day or date stored yet + String date = null; + if (dateMatcher.find()) { + date = dateMatcher.group("date"); + } + + // Description is not mandatory and can be null + String description = null; + if (descriptionMatcher.find()) { + description = descriptionMatcher.group("description"); + } + + // Location is not mandatory and can be null + String location = null; + if (locationMatcher.find()) { + location = descriptionMatcher.group("location"); + } + + if (endTimeMatcher.find()) { + try { + return new AddCommandP( + date, + nameMatcher.group("name"), + location, + description, + startTimeMatcher.group("startTime"), + endTimeMatcher.group("endTime"), + getTagsFromArgs(tagsMatcher.group("tag")), + getRecurrencesFromArgs(recurseMatcher.group("recurrence")) + ); + } catch (IllegalValueException ive) { + return new IncorrectCommandP(ive.getMessage()); + } + } else { + // parse duration string into int + try { + return new AddCommandP( + date, + nameMatcher.group("name"), + location, + description, + startTimeMatcher.group("startTime"), + Integer.parseInt(durationMatcher.group("duration")), + getTagsFromArgs(tagsMatcher.group("tag")), + getRecurrencesFromArgs(recurseMatcher.group("recurrence")) + ); + } catch (IllegalValueException ive) { + return new IncorrectCommandP(ive.getMessage()); + } + } } /** diff --git a/src/planmysem/storage/StorageFileP.java b/src/planmysem/storage/StorageFileP.java index 943e9704c..54652e045 100644 --- a/src/planmysem/storage/StorageFileP.java +++ b/src/planmysem/storage/StorageFileP.java @@ -97,7 +97,7 @@ public void save(Planner planner) throws StorageOperationException { */ public Planner load() throws StorageOperationException { try (final Reader fileReader = - new BufferedReader(new FileReader(path.toFile()))) { + new BufferedReader(new FileReader(path.toFile()))) { final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); final AdaptedPlanner loaded = (AdaptedPlanner) unmarshaller.unmarshal(fileReader); diff --git a/src/planmysem/storage/jaxb/AdaptedSemester.java b/src/planmysem/storage/jaxb/AdaptedSemester.java index 51cccad93..4945d8208 100644 --- a/src/planmysem/storage/jaxb/AdaptedSemester.java +++ b/src/planmysem/storage/jaxb/AdaptedSemester.java @@ -42,8 +42,14 @@ public AdaptedSemester() { public AdaptedSemester(ReadOnlySemester source) { name = source.getName(); academicYear = source.getAcademicYear(); - startDate = source.getStartDate(); - endDate = source.getEndDate(); + // TODO: remove when initialization of semester is complete + if (startDate == null || endDate == null) { + startDate = null; + endDate = null; + } else { + startDate = source.getStartDate().toString(); + endDate = source.getEndDate().toString(); + } noOfWeeks = source.getNoOfWeeks(); days = new HashMap<>(); @@ -84,11 +90,18 @@ public Semester toModelType() throws IllegalValueException { final String endDate = this.endDate; final int noOfWeeks = this.noOfWeeks; - final HashMap days = new HashMap(); + final HashMap days = new HashMap<>(); for (Map.Entry day : this.days.entrySet()) { days.put(day.getKey(), day.getValue().toModelType()); } - return new Semester(name, academicYear, days, startDate, endDate, noOfWeeks); + // TODO: remove after initialization of semester is complete. + if (startDate == null || endDate == null) { + return new Semester(name, academicYear, days, null, null, noOfWeeks); + } else { + return new Semester(name, academicYear, days, + LocalDate.parse(startDate), LocalDate.parse(endDate), noOfWeeks); + } + } } diff --git a/test/java/planmysem/logic/LogicTest.java b/test/java/planmysem/logic/LogicTest.java index da1664221..f1b7c3345 100644 --- a/test/java/planmysem/logic/LogicTest.java +++ b/test/java/planmysem/logic/LogicTest.java @@ -38,6 +38,7 @@ import planmysem.storage.StorageFile; + public class LogicTest { /** @@ -76,19 +77,18 @@ public void execute_invalid() throws Exception { /** * Executes the command and confirms that the result message is correct. * Both the 'address book' and the 'last shown list' are expected to be empty. - * * @see #assertCommandBehavior(String, String, AddressBook, boolean, List) */ private void assertCommandBehavior(String inputCommand, String expectedMessage) throws Exception { - assertCommandBehavior(inputCommand, expectedMessage, AddressBook.empty(), false, Collections.emptyList()); + assertCommandBehavior(inputCommand, expectedMessage, AddressBook.empty(),false, Collections.emptyList()); } /** * Executes the command and confirms that the result message is correct and * also confirms that the following three parts of the Logic object's state are as expected:
- * - the internal address book data are same as those in the {@code expectedAddressBook}
- * - the internal 'last shown list' matches the {@code expectedLastList}
- * - the storage file content matches data in {@code expectedAddressBook}
+ * - the internal address book data are same as those in the {@code expectedAddressBook}
+ * - the internal 'last shown list' matches the {@code expectedLastList}
+ * - the storage file content matches data in {@code expectedAddressBook}
*/ private void assertCommandBehavior(String inputCommand, String expectedMessage, @@ -102,7 +102,7 @@ private void assertCommandBehavior(String inputCommand, //Confirm the result contains the right data assertEquals(expectedMessage, r.feedbackToUser); assertEquals(r.getRelevantPersons().isPresent(), isRelevantPersonsExpected); - if (isRelevantPersonsExpected) { + if(isRelevantPersonsExpected){ assertEquals(lastShownList, r.getRelevantPersons().get()); } @@ -143,25 +143,25 @@ public void execute_clear() throws Exception { public void execute_add_invalidArgsFormat() throws Exception { String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); assertCommandBehavior( - "addDay wrong args wrong args", expectedMessage); + "add wrong args wrong args", expectedMessage); assertCommandBehavior( - "addDay Valid Name 12345 e/valid@email.butNoPhonePrefix a/valid, address", expectedMessage); + "add Valid Name 12345 e/valid@email.butNoPhonePrefix a/valid, address", expectedMessage); assertCommandBehavior( - "addDay Valid Name p/12345 valid@email.butNoPrefix a/valid, address", expectedMessage); + "add Valid Name p/12345 valid@email.butNoPrefix a/valid, address", expectedMessage); assertCommandBehavior( - "addDay Valid Name p/12345 e/valid@email.butNoAddressPrefix valid, address", expectedMessage); + "add Valid Name p/12345 e/valid@email.butNoAddressPrefix valid, address", expectedMessage); } @Test public void execute_add_invalidPersonData() throws Exception { assertCommandBehavior( - "addDay []\\[;] p/12345 e/valid@e.mail a/valid, address", Name.MESSAGE_NAME_CONSTRAINTS); + "add []\\[;] p/12345 e/valid@e.mail a/valid, address", Name.MESSAGE_NAME_CONSTRAINTS); assertCommandBehavior( - "addDay Valid Name p/not_numbers e/valid@e.mail a/valid, address", Phone.MESSAGE_PHONE_CONSTRAINTS); + "add Valid Name p/not_numbers e/valid@e.mail a/valid, address", Phone.MESSAGE_PHONE_CONSTRAINTS); assertCommandBehavior( - "addDay Valid Name p/12345 e/notAnEmail a/valid, address", Email.MESSAGE_EMAIL_CONSTRAINTS); + "add Valid Name p/12345 e/notAnEmail a/valid, address", Email.MESSAGE_EMAIL_CONSTRAINTS); assertCommandBehavior( - "addDay Valid Name p/12345 e/valid@e.mail a/valid, address t/invalid_-[.tag", Tag.MESSAGE_TAG_CONSTRAINTS); + "add Valid Name p/12345 e/valid@e.mail a/valid, address t/invalid_-[.tag", Tag.MESSAGE_TAG_CONSTRAINTS); } @@ -235,7 +235,6 @@ public void execute_view_invalidIndex() throws Exception { /** * Confirms the 'invalid argument index number behaviour' for the given command * targeting a single person in the last shown list, using visible index. - * * @param commandWord to test assuming it targets a single person in the last shown list based on visible index. */ private void assertInvalidIndexBehaviorForCommand(String commandWord) throws Exception { @@ -480,7 +479,7 @@ public void execute_find_matchesIfAnyKeywordPresent() throws Exception { /** * A utility class to generate test data. */ - class TestDataHelper { + class TestDataHelper{ Person adam() throws Exception { Name name = new Name("Adam Brown"); @@ -498,7 +497,7 @@ Person adam() throws Exception { * Running this function with the same parameter values guarantees the returned person will have the same state. * Each unique seed will generate a unique Person object. * - * @param seed used to generate the person data field values + * @param seed used to generate the person data field values * @param isAllFieldsPrivate determines if private-able fields (phone, email, address) will be private */ Person generatePerson(int seed, boolean isAllFieldsPrivate) throws Exception { @@ -511,13 +510,11 @@ Person generatePerson(int seed, boolean isAllFieldsPrivate) throws Exception { ); } - /** - * Generates the correct addDay command based on the person given - */ + /** Generates the correct add command based on the person given */ String generateAddCommand(Person p) { StringJoiner cmd = new StringJoiner(" "); - cmd.add("addDay"); + cmd.add("add"); cmd.add(p.getName().toString()); cmd.add((p.getPhone().isPrivate() ? "pp/" : "p/") + p.getPhone()); @@ -525,7 +522,7 @@ String generateAddCommand(Person p) { cmd.add((p.getAddress().isPrivate() ? "pa/" : "a/") + p.getAddress()); Set tags = p.getTags(); - for (Tag t : tags) { + for(Tag t: tags){ cmd.add("t/" + t.tagName); } @@ -534,11 +531,10 @@ String generateAddCommand(Person p) { /** * Generates an AddressBook with auto-generated persons. - * * @param isPrivateStatuses flags to indicate if all contact details of respective persons should be set to * private. */ - AddressBook generateAddressBook(Boolean... isPrivateStatuses) throws Exception { + AddressBook generateAddressBook(Boolean... isPrivateStatuses) throws Exception{ AddressBook addressBook = new AddressBook(); addToAddressBook(addressBook, isPrivateStatuses); return addressBook; @@ -547,7 +543,7 @@ AddressBook generateAddressBook(Boolean... isPrivateStatuses) throws Exception { /** * Generates an AddressBook based on the list of Persons given. */ - AddressBook generateAddressBook(List persons) throws Exception { + AddressBook generateAddressBook(List persons) throws Exception{ AddressBook addressBook = new AddressBook(); addToAddressBook(addressBook, persons); return addressBook; @@ -555,20 +551,19 @@ AddressBook generateAddressBook(List persons) throws Exception { /** * Adds auto-generated Person objects to the given AddressBook - * - * @param addressBook The AddressBook to which the Persons will be added + * @param addressBook The AddressBook to which the Persons will be added * @param isPrivateStatuses flags to indicate if all contact details of generated persons should be set to * private. */ - void addToAddressBook(AddressBook addressBook, Boolean... isPrivateStatuses) throws Exception { + void addToAddressBook(AddressBook addressBook, Boolean... isPrivateStatuses) throws Exception{ addToAddressBook(addressBook, generatePersonList(isPrivateStatuses)); } /** * Adds the given list of Persons to the given AddressBook */ - void addToAddressBook(AddressBook addressBook, List personsToAdd) throws Exception { - for (Person p : personsToAdd) { + void addToAddressBook(AddressBook addressBook, List personsToAdd) throws Exception{ + for(Person p: personsToAdd){ addressBook.addPerson(p); } } @@ -576,9 +571,9 @@ void addToAddressBook(AddressBook addressBook, List personsToAdd) throws /** * Creates a list of Persons based on the give Person objects. */ - List generatePersonList(Person... persons) throws Exception { + List generatePersonList(Person... persons) throws Exception{ List personList = new ArrayList<>(); - for (Person p : persons) { + for(Person p: persons){ personList.add(p); } return personList; @@ -586,14 +581,13 @@ List generatePersonList(Person... persons) throws Exception { /** * Generates a list of Persons based on the flags. - * * @param isPrivateStatuses flags to indicate if all contact details of respective persons should be set to * private. */ - List generatePersonList(Boolean... isPrivateStatuses) throws Exception { + List generatePersonList(Boolean... isPrivateStatuses) throws Exception{ List persons = new ArrayList<>(); int i = 1; - for (Boolean p : isPrivateStatuses) { + for(Boolean p: isPrivateStatuses){ persons.add(generatePerson(i++, p)); } return persons; @@ -613,4 +607,4 @@ Person generatePersonWithName(String name) throws Exception { } } -} +} \ No newline at end of file diff --git a/test/java/planmysem/parser/ParserTest.java b/test/java/planmysem/parser/ParserTest.java index bc4528415..e79be1b11 100644 --- a/test/java/planmysem/parser/ParserTest.java +++ b/test/java/planmysem/parser/ParserTest.java @@ -34,32 +34,6 @@ public class ParserTest { private Parser parser; - private static Person generateTestPerson() { - try { - return new Person( - new Name(Name.EXAMPLE), - new Phone(Phone.EXAMPLE, true), - new Email(Email.EXAMPLE, false), - new Address(Address.EXAMPLE, true), - new HashSet<>(Arrays.asList(new Tag("tag1"), new Tag("tag2"), new Tag("tag3"))) - ); - } catch (IllegalValueException ive) { - throw new RuntimeException("test person data should be valid by definition"); - } - } - - private static String convertPersonToAddCommandString(ReadOnlyPerson person) { - String addCommand = "addDay " - + person.getName().fullName - + (person.getPhone().isPrivate() ? " pp/" : " p/") + person.getPhone().value - + (person.getEmail().isPrivate() ? " pe/" : " e/") + person.getEmail().value - + (person.getAddress().isPrivate() ? " pa/" : " a/") + person.getAddress().value; - for (Tag tag : person.getTags()) { - addCommand += " t/" + tag.tagName; - } - return addCommand; - } - @Before public void setup() { parser = new Parser(); @@ -67,7 +41,7 @@ public void setup() { @Test public void emptyInput_returnsIncorrect() { - final String[] emptyInputs = {"", " ", "\n \n"}; + final String[] emptyInputs = { "", " ", "\n \n" }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, emptyInputs); } @@ -112,14 +86,14 @@ public void exitCommand_parsedCorrectly() { @Test public void deleteCommand_noArgs() { - final String[] inputs = {"delete", "delete "}; + final String[] inputs = { "delete", "delete " }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test public void deleteCommand_argsIsNotSingleNumber() { - final String[] inputs = {"delete notAnumber ", "delete 8*wh12", "delete 1 2 3 4 5"}; + final String[] inputs = { "delete notAnumber ", "delete 8*wh12", "delete 1 2 3 4 5" }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @@ -134,14 +108,14 @@ public void deleteCommand_numericArg_indexParsedCorrectly() { @Test public void viewCommand_noArgs() { - final String[] inputs = {"view", "view "}; + final String[] inputs = { "view", "view " }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @Test public void viewCommand_argsIsNotSingleNumber() { - final String[] inputs = {"view notAnumber ", "view 8*wh12", "view 1 2 3 4 5"}; + final String[] inputs = { "view notAnumber ", "view 8*wh12", "view 1 2 3 4 5" }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @@ -156,7 +130,7 @@ public void viewCommand_numericArg_indexParsedCorrectly() { @Test public void viewAllCommand_noArgs() { - final String[] inputs = {"viewall", "viewall "}; + final String[] inputs = { "viewall", "viewall " }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewAllCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); @@ -164,7 +138,7 @@ public void viewAllCommand_noArgs() { @Test public void viewAllCommand_argsIsNotSingleNumber() { - final String[] inputs = {"viewall notAnumber ", "viewall 8*wh12", "viewall 1 2 3 4 5"}; + final String[] inputs = { "viewall notAnumber ", "viewall 8*wh12", "viewall 1 2 3 4 5" }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewAllCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); } @@ -195,7 +169,7 @@ public void findCommand_invalidArgs() { @Test public void findCommand_validArgs_parsedCorrectly() { - final String[] keywords = {"key1", "key2", "key3"}; + final String[] keywords = { "key1", "key2", "key3" }; final Set keySet = new HashSet<>(Arrays.asList(keywords)); final String input = "find " + String.join(" ", keySet); @@ -206,7 +180,7 @@ public void findCommand_validArgs_parsedCorrectly() { @Test public void findCommand_duplicateKeys_parsedCorrectly() { - final String[] keywords = {"key1", "key2", "key3"}; + final String[] keywords = { "key1", "key2", "key3" }; final Set keySet = new HashSet<>(Arrays.asList(keywords)); // duplicate every keyword @@ -217,21 +191,21 @@ public void findCommand_duplicateKeys_parsedCorrectly() { } /** - * Test addDay person command + * Test add person command */ @Test public void addCommand_invalidArgs() { final String[] inputs = { - "addDay", - "addDay ", - "addDay wrong args format", + "add", + "add ", + "add wrong args format", // no phone prefix - String.format("addDay $s $s e/$s a/$s", Name.EXAMPLE, Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE), + String.format("add $s $s e/$s a/$s", Name.EXAMPLE, Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE), // no email prefix - String.format("addDay $s p/$s $s a/$s", Name.EXAMPLE, Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE), + String.format("add $s p/$s $s a/$s", Name.EXAMPLE, Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE), // no address prefix - String.format("addDay $s p/$s e/$s $s", Name.EXAMPLE, Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE) + String.format("add $s p/$s e/$s $s", Name.EXAMPLE, Phone.EXAMPLE, Email.EXAMPLE, Address.EXAMPLE) }; final String resultMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); parseAndAssertIncorrectWithMessage(resultMessage, inputs); @@ -248,7 +222,7 @@ public void addCommand_invalidPersonDataInArgs() { final String invalidTagArg = "t/invalid_-[.tag"; // address can be any string, so no invalid address - final String addCommandFormatString = "addDay $s $s $s a/" + Address.EXAMPLE; + final String addCommandFormatString = "add $s $s $s a/" + Address.EXAMPLE; // test each incorrect person data field argument individually final String[] inputs = { @@ -287,6 +261,32 @@ public void addCommand_duplicateTags_merged() throws IllegalValueException { assertEquals(result.getPerson(), testPerson); } + private static Person generateTestPerson() { + try { + return new Person( + new Name(Name.EXAMPLE), + new Phone(Phone.EXAMPLE, true), + new Email(Email.EXAMPLE, false), + new Address(Address.EXAMPLE, true), + new HashSet<>(Arrays.asList(new Tag("tag1"), new Tag("tag2"), new Tag("tag3"))) + ); + } catch (IllegalValueException ive) { + throw new RuntimeException("test person data should be valid by definition"); + } + } + + private static String convertPersonToAddCommandString(ReadOnlyPerson person) { + String addCommand = "add " + + person.getName().fullName + + (person.getPhone().isPrivate() ? " pp/" : " p/") + person.getPhone().value + + (person.getEmail().isPrivate() ? " pe/" : " e/") + person.getEmail().value + + (person.getAddress().isPrivate() ? " pa/" : " a/") + person.getAddress().value; + for (Tag tag : person.getTags()) { + addCommand += " t/" + tag.tagName; + } + return addCommand; + } + /** * Utility methods */ @@ -304,7 +304,7 @@ private void parseAndAssertIncorrectWithMessage(String feedbackMessage, String.. /** * Utility method for parsing input and asserting the class/type of the returned command object. * - * @param input to be parsed + * @param input to be parsed * @param expectedCommandClass expected class of returned command * @return the parsed command object */ @@ -313,4 +313,4 @@ private T parseAndAssertCommandType(String input, Class e assertTrue(result.getClass().isAssignableFrom(expectedCommandClass)); return (T) result; } -} +} \ No newline at end of file