diff --git a/doc/DeveloperGuide.md b/doc/DeveloperGuide.md index c82c3afb9..11e57faec 100644 --- a/doc/DeveloperGuide.md +++ b/doc/DeveloperGuide.md @@ -45,8 +45,10 @@ Priority | As a ... | I want to ... | So that I can... `* * *` | user | add a new person | `* * *` | user | delete a person | remove entries that I no longer need `* * *` | user | find a person by name | locate details of persons without having to go through the entire list +`* * *` | user | find a person by contact details | locate details of persons by entering phone number or email `* *` | user | hide [private contact details](#private-contact-detail) by default | minimize chance of someone else seeing them by accident `*` | user with many persons in the address book | sort persons by name | locate a person easily +`*` | user | see suggestions of tags as I type (by some shortcut keys) | quickly type the commands easily without hard memorizing them ## Appendix B : Use Cases @@ -74,12 +76,41 @@ Use case ends. > 3a1. AddressBook shows an error message
Use case resumes at step 2 +### Use case: Renaming of an existing tag + +**MSS** + +1. User requests to see the list of existing tags. +2. AddressBook shows a list of tags. +3. User requests to rename an specific tag in the list. +4. AddressBook changes the old tag to the new tag that the user specifies + +**Extensions** + +2a. The tag list is empty + +> Use case ends + +3a. The old tag was typed wrongly + +> 3a1. AddressBook shows an error message
+ Use case resumes at step 2 + +3b. The new tag was invalid + +> 3b1. AddressBook shows an error message
+ Use case resumes at step 3 + + ## Appendix C : Non Functional Requirements 1. Should work on any [mainstream OS](#mainstream-os) as long as it has Java 8 or higher installed. 2. Should be able to hold up to 1000 persons. 3. Should come with automated unit tests and open source code. 4. Should favor DOS style commands over Unix-style commands. +5. Should support international characters for all functionalities +6. Should quickly display the search result in last than a second. +7. Should have a tutorial in each major language of users ## Appendix D : Glossary diff --git a/src/seedu/addressbook/commands/AddCommand.java b/src/seedu/addressbook/commands/AddCommand.java index b0f202d42..b955e94bb 100644 --- a/src/seedu/addressbook/commands/AddCommand.java +++ b/src/seedu/addressbook/commands/AddCommand.java @@ -31,6 +31,14 @@ public class AddCommand extends Command { * * @throws IllegalValueException if any of the raw values are invalid */ + + private final boolean isMutating = true; + + @Override + public boolean isMutating(){ + return isMutating; + } + public AddCommand(String name, String phone, boolean isPhonePrivate, String email, boolean isEmailPrivate, @@ -59,7 +67,9 @@ public ReadOnlyPerson getPerson() { @Override public CommandResult execute() { - try { + try { + addressBook.backup(); + addressBook.addPerson(toAdd); return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); } catch (UniquePersonList.DuplicatePersonException dpe) { diff --git a/src/seedu/addressbook/commands/ClearCommand.java b/src/seedu/addressbook/commands/ClearCommand.java index 330146aaa..5af59ff6d 100644 --- a/src/seedu/addressbook/commands/ClearCommand.java +++ b/src/seedu/addressbook/commands/ClearCommand.java @@ -13,7 +13,15 @@ public class ClearCommand extends Command { @Override public CommandResult execute() { + addressBook.backup(); addressBook.clear(); return new CommandResult(MESSAGE_SUCCESS); } + + private final boolean isMutating = true; + + @Override + public boolean isMutating(){ + return isMutating; + } } diff --git a/src/seedu/addressbook/commands/Command.java b/src/seedu/addressbook/commands/Command.java index ccd4fcbf4..e9fbd7f80 100644 --- a/src/seedu/addressbook/commands/Command.java +++ b/src/seedu/addressbook/commands/Command.java @@ -40,7 +40,12 @@ public static String getMessageForPersonListShownSummary(List personsFound = getPersonsWithNameContainingAnyKeyword(keywords); return new CommandResult(getMessageForPersonListShownSummary(personsFound), personsFound); } + + private final boolean isMutating = false; + + @Override + public boolean isMutating(){ + return isMutating; + } /** * Retrieve all persons in the address book whose names contain some of the specified keywords. @@ -52,5 +59,7 @@ private List getPersonsWithNameContainingAnyKeyword(Set } return matchedPersons; } + + } diff --git a/src/seedu/addressbook/commands/HelpCommand.java b/src/seedu/addressbook/commands/HelpCommand.java index ef2ed7d0e..828c2ba28 100644 --- a/src/seedu/addressbook/commands/HelpCommand.java +++ b/src/seedu/addressbook/commands/HelpCommand.java @@ -25,4 +25,11 @@ public class HelpCommand extends Command { public CommandResult execute() { return new CommandResult(MESSAGE_ALL_USAGES); } + + private final boolean isMutating = false; + + @Override + public boolean isMutating(){ + return isMutating; + } } diff --git a/src/seedu/addressbook/commands/IncorrectCommand.java b/src/seedu/addressbook/commands/IncorrectCommand.java index 81abba7a1..046283a39 100644 --- a/src/seedu/addressbook/commands/IncorrectCommand.java +++ b/src/seedu/addressbook/commands/IncorrectCommand.java @@ -16,5 +16,12 @@ public IncorrectCommand(String feedbackToUser){ public CommandResult execute() { return new CommandResult(feedbackToUser); } + + private final boolean isMutating = false; + + @Override + public boolean isMutating(){ + return isMutating; + } } diff --git a/src/seedu/addressbook/commands/ListCommand.java b/src/seedu/addressbook/commands/ListCommand.java index cb604a8e9..96168e978 100644 --- a/src/seedu/addressbook/commands/ListCommand.java +++ b/src/seedu/addressbook/commands/ListCommand.java @@ -22,4 +22,11 @@ public CommandResult execute() { List allPersons = addressBook.getAllPersons().immutableListView(); return new CommandResult(getMessageForPersonListShownSummary(allPersons), allPersons); } + + private final boolean isMutating = false; + + @Override + public boolean isMutating(){ + return isMutating; + } } diff --git a/src/seedu/addressbook/commands/UndoCommand.java b/src/seedu/addressbook/commands/UndoCommand.java new file mode 100644 index 000000000..7300a44df --- /dev/null +++ b/src/seedu/addressbook/commands/UndoCommand.java @@ -0,0 +1,27 @@ +package seedu.addressbook.commands; + +/** + * Undoes the last command + * + */ +public class UndoCommand extends Command { + + public static final String COMMAND_WORD = "undo"; + public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Undoes the last command that changed the information stored.\n\t" + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_SUCCESS = "The last change of the information in the Address Book has been undone!"; + + @Override + public CommandResult execute() { + addressBook.undo(); + return new CommandResult(MESSAGE_SUCCESS); + } + + private final boolean isMutating = true; + + @Override + public boolean isMutating(){ + return isMutating; + } +} diff --git a/src/seedu/addressbook/commands/ViewAllCommand.java b/src/seedu/addressbook/commands/ViewAllCommand.java index ed2c16e83..a69b536ef 100644 --- a/src/seedu/addressbook/commands/ViewAllCommand.java +++ b/src/seedu/addressbook/commands/ViewAllCommand.java @@ -37,4 +37,11 @@ public CommandResult execute() { return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } } + + private final boolean isMutating = false; + + @Override + public boolean isMutating(){ + return isMutating; + } } diff --git a/src/seedu/addressbook/commands/ViewCommand.java b/src/seedu/addressbook/commands/ViewCommand.java index 1058c4b52..ac5b753b3 100644 --- a/src/seedu/addressbook/commands/ViewCommand.java +++ b/src/seedu/addressbook/commands/ViewCommand.java @@ -37,5 +37,12 @@ public CommandResult execute() { return new CommandResult(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } } + + private final boolean isMutating = false; + + @Override + public boolean isMutating(){ + return isMutating; + } } diff --git a/src/seedu/addressbook/data/AddressBook.java b/src/seedu/addressbook/data/AddressBook.java index 7af05d271..b823bc6dd 100644 --- a/src/seedu/addressbook/data/AddressBook.java +++ b/src/seedu/addressbook/data/AddressBook.java @@ -105,6 +105,22 @@ public void clear() { allPersons.clear(); allTags.clear(); } + + /** + * Backs up all persons and tags from the address book. + */ + public void backup(){ + allPersons.backup(); + allTags.backup(); + } + + /** + * Undoes the last change to the address book. + */ + public void undo(){ + allPersons.undo(); + allTags.undo(); + } /** * Defensively copied UniquePersonList of all persons in the address book at the time of the call. diff --git a/src/seedu/addressbook/data/person/UniquePersonList.java b/src/seedu/addressbook/data/person/UniquePersonList.java index c4848a1b4..dce78c9cf 100644 --- a/src/seedu/addressbook/data/person/UniquePersonList.java +++ b/src/seedu/addressbook/data/person/UniquePersonList.java @@ -29,11 +29,14 @@ protected DuplicatePersonException() { public static class PersonNotFoundException extends Exception {} private final List internalList = new ArrayList<>(); + private final List internalBackupList; /** * Constructs empty person list. */ - public UniquePersonList() {} + public UniquePersonList() { + internalBackupList = new ArrayList<>(internalList); + } /** * Constructs a person list with the given persons. @@ -44,6 +47,7 @@ public UniquePersonList(Person... persons) throws DuplicatePersonException { throw new DuplicatePersonException(); } internalList.addAll(initialTags); + internalBackupList = new ArrayList<>(internalList); } /** @@ -56,6 +60,8 @@ public UniquePersonList(Collection persons) throws DuplicatePersonExcept throw new DuplicatePersonException(); } internalList.addAll(persons); + internalBackupList = new ArrayList<>(internalList); + } /** @@ -63,6 +69,8 @@ public UniquePersonList(Collection persons) throws DuplicatePersonExcept */ public UniquePersonList(UniquePersonList source) { internalList.addAll(source.internalList); + internalBackupList = new ArrayList<>(internalList); + } /** @@ -112,6 +120,27 @@ public void remove(ReadOnlyPerson toRemove) throws PersonNotFoundException { public void clear() { internalList.clear(); } + + /** + * Backs up all persons in list. + */ + public void backup(){ + internalBackupList.clear(); + for (Person person : internalList) + internalBackupList.add(person); + } + + /** + * Undoes the latest changes to the person list. + */ + public void undo(){ + internalList.clear(); + for (Person person : internalBackupList) + internalList.add(person); + } + + + @Override public Iterator iterator() { diff --git a/src/seedu/addressbook/data/tag/UniqueTagList.java b/src/seedu/addressbook/data/tag/UniqueTagList.java index 65f37e261..4e8faf18a 100644 --- a/src/seedu/addressbook/data/tag/UniqueTagList.java +++ b/src/seedu/addressbook/data/tag/UniqueTagList.java @@ -2,6 +2,7 @@ import seedu.addressbook.common.Utils; import seedu.addressbook.data.exception.DuplicateDataException; +import seedu.addressbook.data.person.Person; import java.util.*; @@ -29,6 +30,7 @@ protected DuplicateTagException() { public static class TagNotFoundException extends Exception {} private final List internalList = new ArrayList<>(); + private final List internalBackupList= new ArrayList<>(); /** * Constructs an empty TagList. @@ -138,6 +140,26 @@ public void remove(Tag toRemove) throws TagNotFoundException { public void clear() { internalList.clear(); } + + + /** + * Backs up all tags in list. + */ + public void backup(){ + internalBackupList.clear(); + for (Tag tag : internalList) + internalBackupList.add(tag); + } + + /** + * Undoes the latest changes to the tag list. + */ + public void undo(){ + internalList.clear(); + for (Tag tag : internalBackupList) + internalList.add(tag); + } + /** * Replaces the Tags in this list with those in the argument tag list. diff --git a/src/seedu/addressbook/logic/Logic.java b/src/seedu/addressbook/logic/Logic.java index 17afd61a0..13b189cb0 100644 --- a/src/seedu/addressbook/logic/Logic.java +++ b/src/seedu/addressbook/logic/Logic.java @@ -5,7 +5,7 @@ import seedu.addressbook.data.AddressBook; import seedu.addressbook.data.person.ReadOnlyPerson; import seedu.addressbook.parser.Parser; -import seedu.addressbook.storage.StorageFile; +import seedu.addressbook.storage.Storage; import java.util.Collections; import java.util.List; @@ -14,10 +14,10 @@ /** * Represents the main Logic of the AddressBook. */ -public class Logic { +public class Logic { - private StorageFile storage; + private Storage storage; private AddressBook addressBook; /** The list of person shown to the user most recently. */ @@ -28,12 +28,12 @@ public Logic() throws Exception{ setAddressBook(storage.load()); } - Logic(StorageFile storageFile, AddressBook addressBook){ + Logic(Storage storageFile, AddressBook addressBook){ setStorage(storageFile); setAddressBook(addressBook); } - void setStorage(StorageFile storage){ + void setStorage(Storage storage){ this.storage = storage; } @@ -43,10 +43,10 @@ void setAddressBook(AddressBook addressBook){ /** * Creates the StorageFile object based on the user specified path (if any) or the default storage path. - * @throws StorageFile.InvalidStorageFilePathException if the target file path is incorrect. + * @throws Storage.InvalidStorageFilePathException if the target file path is incorrect. */ - private StorageFile initializeStorage() throws StorageFile.InvalidStorageFilePathException { - return new StorageFile(); + private Storage initializeStorage() throws Storage.InvalidStorageFilePathException { + return Storage.newStorage(); } public String getStorageFilePath() { diff --git a/src/seedu/addressbook/parser/Parser.java b/src/seedu/addressbook/parser/Parser.java index 58f4f7e6c..355bd43fc 100644 --- a/src/seedu/addressbook/parser/Parser.java +++ b/src/seedu/addressbook/parser/Parser.java @@ -65,7 +65,10 @@ public Command parseCommand(String userInput) { case ClearCommand.COMMAND_WORD: return new ClearCommand(); - + + case UndoCommand.COMMAND_WORD: + return new UndoCommand(); + case FindCommand.COMMAND_WORD: return prepareFind(arguments); diff --git a/src/seedu/addressbook/storage/Storage.java b/src/seedu/addressbook/storage/Storage.java new file mode 100644 index 000000000..94ea40e4b --- /dev/null +++ b/src/seedu/addressbook/storage/Storage.java @@ -0,0 +1,36 @@ +package seedu.addressbook.storage; + +import seedu.addressbook.data.AddressBook; +import seedu.addressbook.data.exception.IllegalValueException; + +public interface Storage { + + /** + * Signals that the given file path does not fulfill the storage filepath constraints. + */ + public class InvalidStorageFilePathException extends IllegalValueException { + public InvalidStorageFilePathException(String message) { + super(message); + // TODO Auto-generated constructor stub + }} + /** + * Signals that some error has occured while trying to convert and read/write data between the application + * and the storage file. + */ + public static class StorageOperationException extends Exception { + public StorageOperationException(String message) { + super(message); + } + } + public String getPath(); + public void save(AddressBook addressBook)throws StorageOperationException; + public AddressBook load() throws StorageOperationException ; + public static Storage newStorage() throws InvalidStorageFilePathException { + return new StorageFile(); + } + public static Storage newStorage(String path) throws InvalidStorageFilePathException { + return new StorageFile(path); + } + + +} diff --git a/src/seedu/addressbook/storage/StorageFile.java b/src/seedu/addressbook/storage/StorageFile.java index 693097a86..1f28ad848 100644 --- a/src/seedu/addressbook/storage/StorageFile.java +++ b/src/seedu/addressbook/storage/StorageFile.java @@ -8,45 +8,30 @@ import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; + import java.io.*; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; + + /** * Represents the file used to store address book data. */ -public class StorageFile { +public class StorageFile implements Storage{ /** Default file path used if the user doesn't provide the file name. */ public static final String DEFAULT_STORAGE_FILEPATH = "addressbook.txt"; - /* Note: Note the use of nested classes below. * More info https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html */ - /** - * Signals that the given file path does not fulfill the storage filepath constraints. - */ - public static class InvalidStorageFilePathException extends IllegalValueException { - public InvalidStorageFilePathException(String message) { - super(message); - } - } - - /** - * Signals that some error has occured while trying to convert and read/write data between the application - * and the storage file. - */ - public static class StorageOperationException extends Exception { - public StorageOperationException(String message) { - super(message); - } - } - private final JAXBContext jaxbContext; public final Path path; + /** * @throws InvalidStorageFilePathException if the default path is invalid */ @@ -65,7 +50,7 @@ public StorageFile(String filePath) throws InvalidStorageFilePathException { } path = Paths.get(filePath); - if (!isValidPath(path)) { + if (!(isValidPath(path))) { throw new InvalidStorageFilePathException("Storage file should end with '.txt'"); } } @@ -103,6 +88,8 @@ public void save(AddressBook addressBook) throws StorageOperationException { } } + + /** * Loads data from this storage file. * @@ -145,4 +132,6 @@ public String getPath() { return path.toString(); } + + } diff --git a/test/java/seedu/addressbook/logic/LogicTest.java b/test/java/seedu/addressbook/logic/LogicTest.java index 7efa921ca..4075e771e 100644 --- a/test/java/seedu/addressbook/logic/LogicTest.java +++ b/test/java/seedu/addressbook/logic/LogicTest.java @@ -12,7 +12,7 @@ import seedu.addressbook.data.person.*; import seedu.addressbook.data.tag.Tag; import seedu.addressbook.data.tag.UniqueTagList; -import seedu.addressbook.storage.StorageFile; +import seedu.addressbook.storage.Storage; import java.util.*; @@ -28,13 +28,13 @@ public class LogicTest { @Rule public TemporaryFolder saveFolder = new TemporaryFolder(); - private StorageFile saveFile; + private Storage saveFile; private AddressBook addressBook; private Logic logic; @Before public void setup() throws Exception { - saveFile = new StorageFile(saveFolder.newFile("testSaveFile.txt").getPath()); + saveFile = Storage.newStorage(saveFolder.newFile("testSaveFile.txt").getPath()); addressBook = new AddressBook(); saveFile.save(addressBook); logic = new Logic(saveFile, addressBook); diff --git a/test/java/seedu/addressbook/storage/StorageFileTest.java b/test/java/seedu/addressbook/storage/StorageFileTest.java index 1bd6fcab3..8c232cf26 100644 --- a/test/java/seedu/addressbook/storage/StorageFileTest.java +++ b/test/java/seedu/addressbook/storage/StorageFileTest.java @@ -17,7 +17,7 @@ import seedu.addressbook.data.person.Phone; import seedu.addressbook.data.tag.Tag; import seedu.addressbook.data.tag.UniqueTagList; -import seedu.addressbook.storage.StorageFile.StorageOperationException; +import seedu.addressbook.storage.Storage.StorageOperationException; import static seedu.addressbook.util.TestUtil.assertTextFilesEqual; public class StorageFileTest { @@ -45,7 +45,7 @@ public void constructor_noTxtExtension_exceptionThrown() throws Exception { public void load_invalidFormat_exceptionThrown() throws Exception { // The file contains valid xml data, but does not match the AddressBook class StorageFile storage = getStorage("InvalidData.txt"); - thrown.expect(StorageOperationException.class); + thrown.expect(Storage.StorageOperationException.class); storage.load(); }