From df809d0b4027d9a6f8d9589233df6a1816739f9b Mon Sep 17 00:00:00 2001 From: bryanyee33 Date: Tue, 19 Mar 2024 22:50:27 +0800 Subject: [PATCH 1/5] Add undo command --- .../java/seedu/address/logic/Messages.java | 3 +- .../address/logic/commands/CommandType.java | 6 +++ .../address/logic/commands/ExitCommand.java | 2 +- .../address/logic/commands/UndoCommand.java | 27 ++++++++++ .../java/seedu/address/model/AddressBook.java | 54 +++++++++++++------ src/main/java/seedu/address/model/Model.java | 6 +++ .../seedu/address/model/ModelManager.java | 8 ++- .../exceptions/AddressBookException.java | 10 ++++ .../seedu/address/storage/StorageManager.java | 2 +- .../java/seedu/address/ui/MainWindow.java | 4 +- .../logic/commands/AddCommandTest.java | 6 +++ .../logic/commands/ExitCommandTest.java | 2 +- 12 files changed, 106 insertions(+), 24 deletions(-) create mode 100644 src/main/java/seedu/address/logic/commands/UndoCommand.java create mode 100644 src/main/java/seedu/address/model/exceptions/AddressBookException.java diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index 5cf83e04d7..7cfb254650 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -20,8 +20,7 @@ public class Messages { "Multiple values specified for the following single-valued field(s): "; public static final String MESSAGE_SHOWING_HELP = "Opened help window."; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; - + public static final String MESSAGE_EXITING = "Exiting Address Book as requested ..."; /** * Returns an error message indicating the duplicate prefixes. diff --git a/src/main/java/seedu/address/logic/commands/CommandType.java b/src/main/java/seedu/address/logic/commands/CommandType.java index 02d09cca2b..f61b6d3ede 100644 --- a/src/main/java/seedu/address/logic/commands/CommandType.java +++ b/src/main/java/seedu/address/logic/commands/CommandType.java @@ -52,6 +52,12 @@ public Command createCommand(String arguments) { public Command createCommand(String arguments) { return new HelpCommand(); } + }, + UNDO { + @Override + public Command createCommand(String arguments) { + return new UndoCommand(); + } }; public abstract Command createCommand(String arguments) throws IllegalArgumentException; diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 887b4dbb3d..0142a7424c 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -12,7 +12,7 @@ public class ExitCommand extends Command { @Override public String execute(Model model) { - return Messages.MESSAGE_EXIT_ACKNOWLEDGEMENT; + return Messages.MESSAGE_EXITING; } } diff --git a/src/main/java/seedu/address/logic/commands/UndoCommand.java b/src/main/java/seedu/address/logic/commands/UndoCommand.java new file mode 100644 index 0000000000..9bf0a6f939 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/UndoCommand.java @@ -0,0 +1,27 @@ +package seedu.address.logic.commands; + +import seedu.address.model.Model; +import seedu.address.model.exceptions.AddressBookException; + +/** + * Undoes the latest command + */ +public class UndoCommand extends Command { + + public static final String COMMAND_WORD = "undo"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Undo the latest command.\n" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "Undid the latest command."; + + @Override + public String execute(Model model) { + try { + model.undo(); + return MESSAGE_SUCCESS; + } catch (AddressBookException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 73397161e8..75c89a0783 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -2,10 +2,14 @@ import static java.util.Objects.requireNonNull; +import java.util.ArrayList; import java.util.List; +import com.fasterxml.jackson.annotation.JsonIgnore; + import javafx.collections.ObservableList; import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.exceptions.AddressBookException; import seedu.address.model.person.Person; import seedu.address.model.person.UniquePersonList; @@ -14,38 +18,54 @@ * Duplicates are not allowed (by .isSamePerson comparison) */ public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - - /* - * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication - * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html - * - * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication - * among constructors. - */ - { - persons = new UniquePersonList(); + private static final String UNDO_EXCEPTION = "No previous list to return to."; + private final UniquePersonList persons = new UniquePersonList(); + @JsonIgnore + private final ArrayList personsList = new ArrayList<>(); + @JsonIgnore + private int currIdx = 0; + + public AddressBook() { + personsList.add(new UniquePersonList()); } - public AddressBook() {} - /** * Creates an AddressBook using the Persons in the {@code toBeCopied} */ public AddressBook(ReadOnlyAddressBook toBeCopied) { this(); - resetData(toBeCopied); + persons.setPersons(toBeCopied.getPersonList()); + personsList.get(0).setPersons(persons); // persons and personsList[0] need to be different objects } //// list overwrite operations + /** + * Makes a copy of persons, and stores it in personsList. + */ + private void save() { + UniquePersonList savedList = new UniquePersonList(); + savedList.setPersons(persons); + personsList.add(++currIdx, savedList); + } + + /** + * Undoes the latest change to address book. + */ + public void undo() throws AddressBookException { + if (currIdx == 0) { + throw new AddressBookException(UNDO_EXCEPTION); + } + persons.setPersons(personsList.get(--currIdx)); + } + /** * Replaces the contents of the person list with {@code persons}. * {@code persons} must not contain duplicate persons. */ public void setPersons(List persons) { this.persons.setPersons(persons); + save(); } /** @@ -73,6 +93,7 @@ public boolean hasPerson(Person person) { */ public void addPerson(Person p) { persons.add(p); + save(); } /** @@ -82,8 +103,8 @@ public void addPerson(Person p) { */ public void setPerson(Person target, Person editedPerson) { requireNonNull(editedPerson); - persons.setPerson(target, editedPerson); + save(); } /** @@ -92,6 +113,7 @@ public void setPerson(Person target, Person editedPerson) { */ public void removePerson(Person key) { persons.remove(key); + save(); } //// util methods diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1..d82dac9c66 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -5,6 +5,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; +import seedu.address.model.exceptions.AddressBookException; import seedu.address.model.person.Person; /** @@ -52,6 +53,11 @@ public interface Model { /** Returns the AddressBook */ ReadOnlyAddressBook getAddressBook(); + /** + * Undoes the latest change to address book. + */ + void undo() throws AddressBookException; + /** * Returns true if a person with the same identity as {@code person} exists in the address book. */ diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 57bc563fde..e0d1ff0c15 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -11,6 +11,7 @@ import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; +import seedu.address.model.exceptions.AddressBookException; import seedu.address.model.person.Person; /** @@ -21,7 +22,7 @@ public class ModelManager implements Model { private final AddressBook addressBook; private final UserPrefs userPrefs; - private final FilteredList filteredPersons; + private FilteredList filteredPersons; /** * Initializes a ModelManager with the given addressBook and userPrefs. @@ -87,6 +88,11 @@ public ReadOnlyAddressBook getAddressBook() { return addressBook; } + @Override + public void undo() throws AddressBookException { + addressBook.undo(); + } + @Override public boolean hasPerson(Person person) { requireNonNull(person); diff --git a/src/main/java/seedu/address/model/exceptions/AddressBookException.java b/src/main/java/seedu/address/model/exceptions/AddressBookException.java new file mode 100644 index 0000000000..6c731dd9dd --- /dev/null +++ b/src/main/java/seedu/address/model/exceptions/AddressBookException.java @@ -0,0 +1,10 @@ +package seedu.address.model.exceptions; + +/** + * Represents an error which occurs during execution of AddressBook commands. + */ +public class AddressBookException extends Exception { + public AddressBookException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java index 9aa4491acf..c0bef699b5 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/address/storage/StorageManager.java @@ -57,7 +57,7 @@ public Path getAddressBookFilePath() { @Override public Optional readAddressBook() throws DataLoadingException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); + return readAddressBook(getAddressBookFilePath()); } @Override diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index b6175045d2..5bfa9f2e54 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -185,10 +185,10 @@ private String executeCommand(String commandText) throws CommandException, Parse throw e; } - if (commandResult.equals(Messages.MESSAGE_SHOWING_HELP)) { + if (commandResult == Messages.MESSAGE_SHOWING_HELP) { handleHelp(); } - if (commandResult.equals(Messages.MESSAGE_EXIT_ACKNOWLEDGEMENT)) { + if (commandResult == Messages.MESSAGE_EXITING) { handleExit(); } return commandResult; diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java index cb0d864f7b..5627607961 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java @@ -54,6 +54,7 @@ import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.exceptions.AddressBookException; import seedu.address.model.person.Person; import seedu.address.testutil.PersonBuilder; @@ -311,6 +312,11 @@ public ReadOnlyAddressBook getAddressBook() { throw new AssertionError("This method should not be called."); } + @Override + public void undo() throws AddressBookException { + throw new AddressBookException("This method should not be called."); + } + @Override public boolean hasPerson(Person person) { throw new AssertionError("This method should not be called."); diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java index e1d98afaee..d6204a2107 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java @@ -14,6 +14,6 @@ public class ExitCommandTest { @Test public void execute_exit_success() { - assertCommandSuccess(new ExitCommand(), model, Messages.MESSAGE_EXIT_ACKNOWLEDGEMENT, expectedModel); + assertCommandSuccess(new ExitCommand(), model, Messages.MESSAGE_EXITING, expectedModel); } } From 72fe7fcce97f093d81135804dbb5d0403c8cc238 Mon Sep 17 00:00:00 2001 From: bryanyee33 Date: Wed, 20 Mar 2024 09:50:23 +0800 Subject: [PATCH 2/5] Add JUnit tests for Undo --- .../address/logic/commands/UndoCommand.java | 15 ++-- .../java/seedu/address/model/AddressBook.java | 14 +++- src/main/java/seedu/address/model/Model.java | 9 ++- .../seedu/address/model/ModelManager.java | 8 +- .../exceptions/AddressBookException.java | 10 --- .../exceptions/AddressBookUndoException.java | 10 +++ .../logic/commands/AddCommandTest.java | 10 ++- .../logic/commands/UndoCommandTest.java | 77 +++++++++++++++++++ .../seedu/address/model/AddressBookTest.java | 13 ++++ .../seedu/address/model/ModelManagerTest.java | 24 ++++++ 10 files changed, 162 insertions(+), 28 deletions(-) delete mode 100644 src/main/java/seedu/address/model/exceptions/AddressBookException.java create mode 100644 src/main/java/seedu/address/model/exceptions/AddressBookUndoException.java create mode 100644 src/test/java/seedu/address/logic/commands/UndoCommandTest.java diff --git a/src/main/java/seedu/address/logic/commands/UndoCommand.java b/src/main/java/seedu/address/logic/commands/UndoCommand.java index 9bf0a6f939..6dc63f42c5 100644 --- a/src/main/java/seedu/address/logic/commands/UndoCommand.java +++ b/src/main/java/seedu/address/logic/commands/UndoCommand.java @@ -1,7 +1,7 @@ package seedu.address.logic.commands; +import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.exceptions.AddressBookException; /** * Undoes the latest command @@ -14,14 +14,15 @@ public class UndoCommand extends Command { + "Example: " + COMMAND_WORD; public static final String MESSAGE_SUCCESS = "Undid the latest command."; + public static final String MESSAGE_UNDO_EXCEPTION = "No previous list to return to."; @Override - public String execute(Model model) { - try { - model.undo(); - return MESSAGE_SUCCESS; - } catch (AddressBookException e) { - return e.getMessage(); + public String execute(Model model) throws CommandException { + if (!model.canUndo()) { + throw new CommandException(MESSAGE_UNDO_EXCEPTION); } + + model.undo(); + return MESSAGE_SUCCESS; } } diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 75c89a0783..d9b68303a3 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -9,7 +9,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.util.ToStringBuilder; -import seedu.address.model.exceptions.AddressBookException; +import seedu.address.model.exceptions.AddressBookUndoException; import seedu.address.model.person.Person; import seedu.address.model.person.UniquePersonList; @@ -18,7 +18,6 @@ * Duplicates are not allowed (by .isSamePerson comparison) */ public class AddressBook implements ReadOnlyAddressBook { - private static final String UNDO_EXCEPTION = "No previous list to return to."; private final UniquePersonList persons = new UniquePersonList(); @JsonIgnore private final ArrayList personsList = new ArrayList<>(); @@ -49,12 +48,19 @@ private void save() { personsList.add(++currIdx, savedList); } + /** + * Returns true if there are states to reverse to. + */ + public boolean canUndo() { + return currIdx > 0; + } + /** * Undoes the latest change to address book. */ - public void undo() throws AddressBookException { + public void undo() { if (currIdx == 0) { - throw new AddressBookException(UNDO_EXCEPTION); + throw new AddressBookUndoException(); } persons.setPersons(personsList.get(--currIdx)); } diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d82dac9c66..ba13e20cb9 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -5,7 +5,7 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; -import seedu.address.model.exceptions.AddressBookException; +import seedu.address.model.exceptions.AddressBookUndoException; import seedu.address.model.person.Person; /** @@ -53,10 +53,15 @@ public interface Model { /** Returns the AddressBook */ ReadOnlyAddressBook getAddressBook(); + /** + * Returns true if there are states to reverse to. + */ + boolean canUndo(); + /** * Undoes the latest change to address book. */ - void undo() throws AddressBookException; + void undo() throws AddressBookUndoException; /** * Returns true if a person with the same identity as {@code person} exists in the address book. diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index e0d1ff0c15..6788fc9043 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -11,7 +11,6 @@ import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; -import seedu.address.model.exceptions.AddressBookException; import seedu.address.model.person.Person; /** @@ -89,7 +88,12 @@ public ReadOnlyAddressBook getAddressBook() { } @Override - public void undo() throws AddressBookException { + public boolean canUndo() { + return addressBook.canUndo(); + } + + @Override + public void undo() { addressBook.undo(); } diff --git a/src/main/java/seedu/address/model/exceptions/AddressBookException.java b/src/main/java/seedu/address/model/exceptions/AddressBookException.java deleted file mode 100644 index 6c731dd9dd..0000000000 --- a/src/main/java/seedu/address/model/exceptions/AddressBookException.java +++ /dev/null @@ -1,10 +0,0 @@ -package seedu.address.model.exceptions; - -/** - * Represents an error which occurs during execution of AddressBook commands. - */ -public class AddressBookException extends Exception { - public AddressBookException(String message) { - super(message); - } -} diff --git a/src/main/java/seedu/address/model/exceptions/AddressBookUndoException.java b/src/main/java/seedu/address/model/exceptions/AddressBookUndoException.java new file mode 100644 index 0000000000..62032d53db --- /dev/null +++ b/src/main/java/seedu/address/model/exceptions/AddressBookUndoException.java @@ -0,0 +1,10 @@ +package seedu.address.model.exceptions; + +/** + * Signals that there are no more states to undo to. + */ +public class AddressBookUndoException extends RuntimeException { + public AddressBookUndoException() { + super("There are no previous AddressBook states to return to."); + } +} diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java index 5627607961..9e22132a6b 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java @@ -54,7 +54,6 @@ import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.exceptions.AddressBookException; import seedu.address.model.person.Person; import seedu.address.testutil.PersonBuilder; @@ -313,8 +312,13 @@ public ReadOnlyAddressBook getAddressBook() { } @Override - public void undo() throws AddressBookException { - throw new AddressBookException("This method should not be called."); + public boolean canUndo() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void undo() { + throw new AssertionError("This method should not be called."); } @Override diff --git a/src/test/java/seedu/address/logic/commands/UndoCommandTest.java b/src/test/java/seedu/address/logic/commands/UndoCommandTest.java new file mode 100644 index 0000000000..12cd1c3969 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/UndoCommandTest.java @@ -0,0 +1,77 @@ +package seedu.address.logic.commands; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.person.Person; +import seedu.address.testutil.PersonBuilder; + +public class UndoCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + + @Test + public void execute_multipleInvalidUndo_throwsCommandException() { + UndoCommand undoCommand = new UndoCommand(); + + String expectedMessage = UndoCommand.MESSAGE_UNDO_EXCEPTION; + + assertCommandFailure(undoCommand, model, expectedMessage); + assertCommandFailure(undoCommand, model, expectedMessage); + assertCommandFailure(undoCommand, model, expectedMessage); + } + + @Test + public void execute_oneUndo_success() { + UndoCommand undoCommand = new UndoCommand(); + + String expectedMessage = UndoCommand.MESSAGE_SUCCESS; + + Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + model.deletePerson(personToDelete); + ModelManager expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + + assertCommandSuccess(undoCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_multipleUndo_success() { + UndoCommand undoCommand = new UndoCommand(); + + String expectedMessage = UndoCommand.MESSAGE_SUCCESS; + ModelManager expectedModelBase = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + + // delete + Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + ModelManager expectedModelDelete = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + expectedModelDelete.deletePerson(personToDelete); + model.deletePerson(personToDelete); + + // edit + Person editedPerson = new PersonBuilder().build(); + ModelManager expectedModelEdit = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + expectedModelEdit.setPerson(model.getFilteredPersonList().get(0), editedPerson); + editedPerson = new PersonBuilder().build(); + model.setPerson(model.getFilteredPersonList().get(0), editedPerson); + + // clear + model.setAddressBook(new AddressBook()); + + // undo clear + assertCommandSuccess(undoCommand, model, expectedMessage, expectedModelEdit); + // undo edit + assertCommandSuccess(undoCommand, model, expectedMessage, expectedModelDelete); + // undo delete + assertCommandSuccess(undoCommand, model, expectedMessage, expectedModelBase); + // no more to undo + assertCommandFailure(undoCommand, model, UndoCommand.MESSAGE_UNDO_EXCEPTION); + } +} diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java index 68c8c5ba4d..ba23461c2b 100644 --- a/src/test/java/seedu/address/model/AddressBookTest.java +++ b/src/test/java/seedu/address/model/AddressBookTest.java @@ -1,5 +1,6 @@ package seedu.address.model; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -18,6 +19,7 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import seedu.address.model.exceptions.AddressBookUndoException; import seedu.address.model.person.Person; import seedu.address.model.person.exceptions.DuplicatePersonException; import seedu.address.testutil.PersonBuilder; @@ -31,6 +33,17 @@ public void constructor() { assertEquals(Collections.emptyList(), addressBook.getPersonList()); } + @Test + public void undo_undoRemaining_success() { + addressBook.addPerson(ALICE); + assertDoesNotThrow(addressBook::undo); + } + + @Test + public void undo_noUndoRemaining_throwsAddressBookUndoException() { + assertThrows(AddressBookUndoException.class, addressBook::undo); + } + @Test public void resetData_null_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> addressBook.resetData(null)); diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java index 943ceabc73..05e0ff3324 100644 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ b/src/test/java/seedu/address/model/ModelManagerTest.java @@ -1,5 +1,6 @@ package seedu.address.model; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -15,6 +16,7 @@ import org.junit.jupiter.api.Test; import seedu.address.commons.core.GuiSettings; +import seedu.address.model.exceptions.AddressBookUndoException; import seedu.address.model.person.NameContainsKeywordsPredicate; import seedu.address.testutil.AddressBookBuilder; @@ -72,6 +74,28 @@ public void setAddressBookFilePath_validPath_setsAddressBookFilePath() { assertEquals(path, modelManager.getAddressBookFilePath()); } + @Test + public void canUndo_undoRemaining_returnTrue() { + modelManager.addPerson(ALICE); + assertTrue(modelManager.canUndo()); + } + + @Test + public void canUndo_noUndoRemaining_returnFalse() { + assertFalse(modelManager.canUndo()); + } + + @Test + public void undo_undoRemaining_success() { + modelManager.addPerson(ALICE); + assertDoesNotThrow(() -> modelManager.undo()); + } + + @Test + public void undo_noUndoRemaining_throwsAddressBookUndoException() { + assertThrows(AddressBookUndoException.class, () -> modelManager.undo()); + } + @Test public void hasPerson_nullPerson_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null)); From 5318345b13e51c72a96840cfe09f47464322a093 Mon Sep 17 00:00:00 2001 From: bryanyee33 Date: Wed, 20 Mar 2024 09:54:38 +0800 Subject: [PATCH 3/5] Change Undo response messages --- .../java/seedu/address/logic/commands/UndoCommand.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/UndoCommand.java b/src/main/java/seedu/address/logic/commands/UndoCommand.java index 6dc63f42c5..0b5dbe2022 100644 --- a/src/main/java/seedu/address/logic/commands/UndoCommand.java +++ b/src/main/java/seedu/address/logic/commands/UndoCommand.java @@ -4,17 +4,17 @@ import seedu.address.model.Model; /** - * Undoes the latest command + * Undoes the latest modifying command */ public class UndoCommand extends Command { public static final String COMMAND_WORD = "undo"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Undo the latest command.\n" + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Undo the latest modifying command.\n" + "Example: " + COMMAND_WORD; - public static final String MESSAGE_SUCCESS = "Undid the latest command."; - public static final String MESSAGE_UNDO_EXCEPTION = "No previous list to return to."; + public static final String MESSAGE_SUCCESS = "Undid the latest modifying command."; + public static final String MESSAGE_UNDO_EXCEPTION = "No modifying command to reverse."; @Override public String execute(Model model) throws CommandException { From 8fd02ef2a90862689984b911aaa6b4612384afb8 Mon Sep 17 00:00:00 2001 From: bryanyee33 Date: Wed, 20 Mar 2024 10:04:33 +0800 Subject: [PATCH 4/5] ModelManager::filteredPersons: Change to final --- src/main/java/seedu/address/model/ModelManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 6788fc9043..9a3c9fb900 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -21,7 +21,7 @@ public class ModelManager implements Model { private final AddressBook addressBook; private final UserPrefs userPrefs; - private FilteredList filteredPersons; + private final FilteredList filteredPersons; /** * Initializes a ModelManager with the given addressBook and userPrefs. From 3d6de3e8805783299b73f8af28aa8f2c21464afc Mon Sep 17 00:00:00 2001 From: bryanyee33 Date: Wed, 20 Mar 2024 14:22:16 +0800 Subject: [PATCH 5/5] Change undoList to stack --- .../java/seedu/address/model/AddressBook.java | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index d9b68303a3..dd2bb9f09c 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -2,8 +2,8 @@ import static java.util.Objects.requireNonNull; -import java.util.ArrayList; import java.util.List; +import java.util.Stack; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -20,21 +20,16 @@ public class AddressBook implements ReadOnlyAddressBook { private final UniquePersonList persons = new UniquePersonList(); @JsonIgnore - private final ArrayList personsList = new ArrayList<>(); - @JsonIgnore - private int currIdx = 0; + private final Stack undoList = new Stack<>(); public AddressBook() { - personsList.add(new UniquePersonList()); } /** * Creates an AddressBook using the Persons in the {@code toBeCopied} */ public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); persons.setPersons(toBeCopied.getPersonList()); - personsList.get(0).setPersons(persons); // persons and personsList[0] need to be different objects } //// list overwrite operations @@ -45,24 +40,24 @@ public AddressBook(ReadOnlyAddressBook toBeCopied) { private void save() { UniquePersonList savedList = new UniquePersonList(); savedList.setPersons(persons); - personsList.add(++currIdx, savedList); + undoList.add(savedList); } /** * Returns true if there are states to reverse to. */ public boolean canUndo() { - return currIdx > 0; + return !undoList.empty(); } /** * Undoes the latest change to address book. */ public void undo() { - if (currIdx == 0) { + if (undoList.empty()) { throw new AddressBookUndoException(); } - persons.setPersons(personsList.get(--currIdx)); + persons.setPersons(undoList.pop()); } /** @@ -70,8 +65,8 @@ public void undo() { * {@code persons} must not contain duplicate persons. */ public void setPersons(List persons) { - this.persons.setPersons(persons); save(); + this.persons.setPersons(persons); } /** @@ -98,8 +93,8 @@ public boolean hasPerson(Person person) { * The person must not already exist in the address book. */ public void addPerson(Person p) { - persons.add(p); save(); + persons.add(p); } /** @@ -109,8 +104,8 @@ public void addPerson(Person p) { */ public void setPerson(Person target, Person editedPerson) { requireNonNull(editedPerson); - persons.setPerson(target, editedPerson); save(); + persons.setPerson(target, editedPerson); } /** @@ -118,8 +113,8 @@ public void setPerson(Person target, Person editedPerson) { * {@code key} must exist in the address book. */ public void removePerson(Person key) { - persons.remove(key); save(); + persons.remove(key); } //// util methods