From f5be0202febf4a8cb56e6ecf1c286a0a32bf41f0 Mon Sep 17 00:00:00 2001 From: Vivek Date: Sun, 21 Jan 2018 20:05:45 +0800 Subject: [PATCH 01/41] Replace verbose loops with lambda expressions Many loops are verbose. This leads to longer lines of code that are not concise nor clean. Let's replace these verbose loops with lambda expressions to improve code quality. --- .../java/seedu/address/commons/core/LogsCenter.java | 8 +++----- .../seedu/address/commons/util/CollectionUtil.java | 11 +---------- .../java/seedu/address/commons/util/StringUtil.java | 9 +++------ .../address/logic/parser/ArgumentTokenizer.java | 12 +++++------- .../seedu/address/model/util/SampleDataUtil.java | 12 +++++------- .../java/seedu/address/storage/XmlAdaptedPerson.java | 8 ++++---- 6 files changed, 21 insertions(+), 39 deletions(-) diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/address/commons/core/LogsCenter.java index 46e4c3aac468..5316a1d87d3e 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/address/commons/core/LogsCenter.java @@ -1,9 +1,9 @@ package seedu.address.commons.core; import java.io.IOException; +import java.util.Arrays; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; -import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; @@ -76,10 +76,8 @@ private static void addConsoleHandler(Logger logger) { * Remove all the handlers from {@code logger}. */ private static void removeHandlers(Logger logger) { - Handler[] handlers = logger.getHandlers(); - for (Handler handler : handlers) { - logger.removeHandler(handler); - } + Arrays.stream(logger.getHandlers()) + .forEach(logger::removeHandler); } /** diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/address/commons/util/CollectionUtil.java index 52d209e778dd..478ba077c2ef 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/address/commons/util/CollectionUtil.java @@ -4,9 +4,7 @@ import java.util.Arrays; import java.util.Collection; -import java.util.HashSet; import java.util.Objects; -import java.util.Set; import java.util.stream.Stream; /** @@ -39,13 +37,6 @@ public static boolean isAnyNonNull(Object... items) { * Returns true if every element in a collection are unique by {@link Object#equals(Object)}. */ public static boolean elementsAreUnique(Collection items) { - final Set testSet = new HashSet<>(); - for (Object item : items) { - final boolean itemAlreadyExists = !testSet.add(item); // see Set documentation - if (itemAlreadyExists) { - return false; - } - } - return true; + return items.size() == items.stream().distinct().count(); } } diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java index 6e403c17c96e..61cc8c9a1cb8 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/address/commons/util/StringUtil.java @@ -5,6 +5,7 @@ import java.io.PrintWriter; import java.io.StringWriter; +import java.util.Arrays; /** * Helper functions for handling strings. @@ -33,12 +34,8 @@ public static boolean containsWordIgnoreCase(String sentence, String word) { String preppedSentence = sentence; String[] wordsInPreppedSentence = preppedSentence.split("\\s+"); - for (String wordInSentence: wordsInPreppedSentence) { - if (wordInSentence.equalsIgnoreCase(preppedWord)) { - return true; - } - } - return false; + return Arrays.stream(wordsInPreppedSentence) + .anyMatch(preppedWord::equalsIgnoreCase); } /** diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java index a1bddbb6b979..e473769ec522 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java @@ -1,7 +1,9 @@ package seedu.address.logic.parser; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; /** * Tokenizes arguments string of the form: {@code preamble value value ...}
@@ -34,13 +36,9 @@ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) { * @return List of zero-based prefix positions in the given arguments string */ private static List findAllPrefixPositions(String argsString, Prefix... prefixes) { - List positions = new ArrayList<>(); - - for (Prefix prefix : prefixes) { - positions.addAll(findPrefixPositions(argsString, prefix)); - } - - return positions; + return Arrays.stream(prefixes) + .flatMap(prefix -> findPrefixPositions(argsString, prefix).stream()) + .collect(Collectors.toList()); } /** diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index aea96bfb31f3..31d4034a3845 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -1,7 +1,8 @@ package seedu.address.model.util; -import java.util.HashSet; +import java.util.Arrays; import java.util.Set; +import java.util.stream.Collectors; import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; @@ -56,12 +57,9 @@ public static ReadOnlyAddressBook getSampleAddressBook() { * Returns a tag set containing the list of strings given. */ public static Set getTagSet(String... strings) { - HashSet tags = new HashSet<>(); - for (String s : strings) { - tags.add(new Tag(s)); - } - - return tags; + return Arrays.stream(strings) + .map(Tag::new) + .collect(Collectors.toSet()); } } diff --git a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java b/src/main/java/seedu/address/storage/XmlAdaptedPerson.java index 87120ddbaa5a..c36205a983e6 100644 --- a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java +++ b/src/main/java/seedu/address/storage/XmlAdaptedPerson.java @@ -4,6 +4,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import javax.xml.bind.annotation.XmlElement; @@ -61,10 +62,9 @@ public XmlAdaptedPerson(Person source) { phone = source.getPhone().value; email = source.getEmail().value; address = source.getAddress().value; - tagged = new ArrayList<>(); - for (Tag tag : source.getTags()) { - tagged.add(new XmlAdaptedTag(tag)); - } + tagged = source.getTags().stream() + .map(XmlAdaptedTag::new) + .collect(Collectors.toList()); } /** From cf8761d36dd906123d37b255eb20358e6e13f799 Mon Sep 17 00:00:00 2001 From: Vivek Date: Sun, 21 Jan 2018 20:07:22 +0800 Subject: [PATCH 02/41] UniquePersonList#setPersons(List): refactor verbose loop The for loop in UniquePersonList#setPersons(List) is verbose and it can be simplified. Since lambda expressions cannot handle exceptions, this loop cannot be replaced by a lambda expression and needs to be simplified instead. Let's simplify this for loop using CollectionUtil#elementsAreUnique(Collection). --- .../java/seedu/address/model/person/UniquePersonList.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java index f2c4c4c585e4..0a32534dbfb8 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/seedu/address/model/person/UniquePersonList.java @@ -87,11 +87,11 @@ public void setPersons(UniquePersonList replacement) { public void setPersons(List persons) throws DuplicatePersonException { requireAllNonNull(persons); - final UniquePersonList replacement = new UniquePersonList(); - for (final Person person : persons) { - replacement.add(person); + if (!CollectionUtil.elementsAreUnique(persons)) { + throw new DuplicatePersonException(); } - setPersons(replacement); + + this.internalList.setAll(persons); } /** From 0cc5f3991569ca71853e182b8baa7c8e15e3c14e Mon Sep 17 00:00:00 2001 From: nicholaschuayunzhi Date: Wed, 28 Feb 2018 15:05:36 +0800 Subject: [PATCH 03/41] AddressBook#removePerson(Person): update return type AddressBook#removePerson(Person) returns true if the person passed into this method can be found in the internal list, and throws a PersonNotFoundException otherwise. Returning a boolean is not required as the thrown exception would indicate that the person cannot be found. Let's update the return type for AddressBook#removePerson(Person) to void. --- src/main/java/seedu/address/model/AddressBook.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index f8d0260de159..7251d520bbaf 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -139,12 +139,8 @@ private Person syncWithMasterTagList(Person person) { * Removes {@code key} from this {@code AddressBook}. * @throws PersonNotFoundException if the {@code key} is not in this {@code AddressBook}. */ - public boolean removePerson(Person key) throws PersonNotFoundException { - if (persons.remove(key)) { - return true; - } else { - throw new PersonNotFoundException(); - } + public void removePerson(Person key) throws PersonNotFoundException { + persons.remove(key); } //// tag-level operations From f09009030214ae714fc7169ef281c02fa885472c Mon Sep 17 00:00:00 2001 From: nicholaschuayunzhi Date: Wed, 28 Feb 2018 14:48:10 +0800 Subject: [PATCH 04/41] UniquePersonList#remove(Person): update return type UniquePersonList#remove(Person) returns true if the person passed into this method can be found in the internal list, and false otherwise. It also throws PersonNotFoundException if a person is not found. Returning a boolean is not required as the exception is thrown before the value is returned. Let's update the return type for UniquePersonList#remove(Person) to void. --- .../java/seedu/address/model/person/UniquePersonList.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java index 0a32534dbfb8..37f4fdfd34d7 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/seedu/address/model/person/UniquePersonList.java @@ -72,13 +72,11 @@ public void setPerson(Person target, Person editedPerson) * * @throws PersonNotFoundException if no such person could be found in the list. */ - public boolean remove(Person toRemove) throws PersonNotFoundException { + public void remove(Person toRemove) throws PersonNotFoundException { requireNonNull(toRemove); - final boolean personFoundAndDeleted = internalList.remove(toRemove); - if (!personFoundAndDeleted) { + if (!internalList.remove(toRemove)) { throw new PersonNotFoundException(); } - return personFoundAndDeleted; } public void setPersons(UniquePersonList replacement) { From 088db25ef7d977f3aae56a71324cd1d8879ca1ee Mon Sep 17 00:00:00 2001 From: Jun An Date: Fri, 16 Feb 2018 22:57:51 +0800 Subject: [PATCH 05/41] MainWindow.fxml: update onCloseRequest event to call handleExit() The MainWindow does not have an event handler to handle external requests (such as the close button or through the taskbar) to close the MainWindow. This may cause the app to not close properly since MainApp#stop() is not called as the ExitAppRequestEvent is not raised. MainApp#stop() is the method used to call Platform#exit() and System#exit(int) which closes our app and all remaining open windows. Thus, if MainApp#stop() is not called, the app may continue to run even after the MainWindow is closed, causing existing HelpWindows to remain open even when the user expects the app to stop. According to the life-cycle section in the documentation on JavaFX Application[1], the app will only finish and call MainApp#stop() when the app either calls Platform#exit() or when the last window has been closed. Thus, should there be any HelpWindows still opened, the app will continue running, causing those HelpWindows to remain open. Let's add an event handler for the onCloseRequest event in the root of MainWindow.fxml. This event will call MainWindow#handleExit(), which raises an ExitAppRequestEvent, when there is an external request to close the app. EmptyMainWindowHandle does not contain a close button, thus to test if an ExitAppRequestEvent was raised, we fire a WindowEvent#WINDOW_CLOSE_REQUEST event which will trigger the onCloseRequest event. [1] JavaFx documentation for Application: https://docs.oracle.com/javafx/2/api/javafx/application/Application.html --- src/main/resources/view/MainWindow.fxml | 2 +- .../seedu/address/ui/MainWindowCloseTest.java | 83 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/test/java/seedu/address/ui/MainWindowCloseTest.java diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 1dadb95b6ffe..daf386d8f5b8 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -12,7 +12,7 @@ + minWidth="450" minHeight="600" onCloseRequest="#handleExit"> diff --git a/src/test/java/seedu/address/ui/MainWindowCloseTest.java b/src/test/java/seedu/address/ui/MainWindowCloseTest.java new file mode 100644 index 000000000000..6321f255b048 --- /dev/null +++ b/src/test/java/seedu/address/ui/MainWindowCloseTest.java @@ -0,0 +1,83 @@ +package seedu.address.ui; + +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.testfx.api.FxToolkit; + +import guitests.guihandles.StageHandle; +import javafx.stage.Stage; +import javafx.stage.WindowEvent; +import seedu.address.commons.core.Config; +import seedu.address.commons.events.ui.ExitAppRequestEvent; +import seedu.address.logic.LogicManager; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.ui.testutil.EventsCollectorRule; + +/** + * Contains tests for closing of the {@code MainWindow}. + */ +public class MainWindowCloseTest extends GuiUnitTest { + @Rule + public final EventsCollectorRule eventsCollectorRule = new EventsCollectorRule(); + + private MainWindow mainWindow; + private EmptyMainWindowHandle mainWindowHandle; + private Stage stage; + + @Before + public void setUp() throws Exception { + FxToolkit.setupStage(stage -> { + this.stage = stage; + mainWindow = new MainWindow(stage, new Config(), new UserPrefs(), new LogicManager(new ModelManager())); + mainWindowHandle = new EmptyMainWindowHandle(stage); + + stage.setScene(mainWindow.getRoot().getScene()); + mainWindowHandle.focus(); + }); + FxToolkit.showStage(); + } + + @Test + public void close_menuBarExitButton_exitAppRequestEventPosted() { + mainWindowHandle.clickOnMenuExitButton(); + assertTrue(eventsCollectorRule.eventsCollector.getMostRecent() instanceof ExitAppRequestEvent); + assertTrue(eventsCollectorRule.eventsCollector.getSize() == 1); + } + + @Test + public void close_externalRequest_exitAppRequestEventPosted() { + mainWindowHandle.closeMainWindowExternally(); + assertTrue(eventsCollectorRule.eventsCollector.getMostRecent() instanceof ExitAppRequestEvent); + assertTrue(eventsCollectorRule.eventsCollector.getSize() == 1); + } + + /** + * A handle for an empty {@code MainWindow}. The components in {@code MainWindow} are not initialized. + */ + private class EmptyMainWindowHandle extends StageHandle { + + private EmptyMainWindowHandle(Stage stage) { + super(stage); + } + + /** + * Closes the {@code MainWindow} by clicking on the menu bar's exit button. + */ + private void clickOnMenuExitButton() { + guiRobot.clickOn("File"); + guiRobot.clickOn("Exit"); + } + + /** + * Closes the {@code MainWindow} through an external request {@code MainWindow} (e.g pressing the 'X' button on + * the {@code MainWindow} or closing the app through the taskbar). + */ + private void closeMainWindowExternally() { + guiRobot.interact(() -> stage.fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST))); + } + } +} From da85890378c6266ab3073bff2cb5389195d079a6 Mon Sep 17 00:00:00 2001 From: zhuleyan <1155077028@link.cuhk.edu.hk> Date: Wed, 7 Mar 2018 00:05:31 +0800 Subject: [PATCH 06/41] change repoURL --- docs/DeveloperGuide.adoc | 2 +- docs/UserGuide.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index 1733af113b29..b77257950761 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -10,7 +10,7 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level4/tree/master +:repoURL: https://github.com/zhuleyan/addressbook-level4/tree/master By: `Team SE-EDU`      Since: `Jun 2016`      Licence: `MIT` diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 74248917e438..b3d6e5ec6b7b 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -11,7 +11,7 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level4 +:repoURL: https://github.com/zhuleyan/addressbook-level4 By: `Team SE-EDU` Since: `Jun 2016` Licence: `MIT` From acca0226556e61df94fee0b7be80b9efeb4de446 Mon Sep 17 00:00:00 2001 From: "DESKTOP-9K2NI32\\Liu Yiming" Date: Fri, 9 Mar 2018 12:28:06 +0800 Subject: [PATCH 07/41] updates DeveloperGuide and UserGuide repo URL --- docs/DeveloperGuide.adoc | 2 +- docs/UserGuide.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index 1733af113b29..f938095f926e 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -10,7 +10,7 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level4/tree/master +:repoURL: https://github.com/AddressBook-F12-A2/main/tree/master By: `Team SE-EDU`      Since: `Jun 2016`      Licence: `MIT` diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 74248917e438..cef8e0952e37 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -11,7 +11,7 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/se-edu/addressbook-level4 +:repoURL: https://github.com/AddressBook-F12-A2/main By: `Team SE-EDU` Since: `Jun 2016` Licence: `MIT` From c6f99b17a5b44c5f5474badd8492f6b0e4e9b198 Mon Sep 17 00:00:00 2001 From: zhuleyan <1155077028@link.cuhk.edu.hk> Date: Fri, 9 Mar 2018 12:29:54 +0800 Subject: [PATCH 08/41] edit repoURL --- docs/DeveloperGuide.adoc | 2 +- docs/UserGuide.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index b77257950761..80d5c54f2ee4 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -10,7 +10,7 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/zhuleyan/addressbook-level4/tree/master +:repoURL: https://github.com/CS2103JAN2018-F12-B1/main/tree/master By: `Team SE-EDU`      Since: `Jun 2016`      Licence: `MIT` diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index b3d6e5ec6b7b..bfdcc4095be8 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -11,7 +11,7 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/zhuleyan/addressbook-level4 +:repoURL: https://github.com/CS2103JAN2018-F12-B1/main By: `Team SE-EDU` Since: `Jun 2016` Licence: `MIT` From de4bad7e42e221f17510bf828de1a2dfe4022ac6 Mon Sep 17 00:00:00 2001 From: "DESKTOP-9K2NI32\\Liu Yiming" Date: Fri, 9 Mar 2018 12:39:41 +0800 Subject: [PATCH 09/41] corrects typo for UserGuide & DeveloperGuide repo URL --- docs/DeveloperGuide.adoc | 2 +- docs/UserGuide.adoc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index f938095f926e..80d5c54f2ee4 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -10,7 +10,7 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/AddressBook-F12-A2/main/tree/master +:repoURL: https://github.com/CS2103JAN2018-F12-B1/main/tree/master By: `Team SE-EDU`      Since: `Jun 2016`      Licence: `MIT` diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index cef8e0952e37..bfdcc4095be8 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -11,7 +11,7 @@ ifdef::env-github[] :tip-caption: :bulb: :note-caption: :information_source: endif::[] -:repoURL: https://github.com/AddressBook-F12-A2/main +:repoURL: https://github.com/CS2103JAN2018-F12-B1/main By: `Team SE-EDU` Since: `Jun 2016` Licence: `MIT` From 5b5b3a560e822a21810c442193beb2a190e78bf5 Mon Sep 17 00:00:00 2001 From: zhuleyan <1155077028@link.cuhk.edu.hk> Date: Fri, 9 Mar 2018 13:04:56 +0800 Subject: [PATCH 10/41] test --- docs/UserGuide.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index b3d6e5ec6b7b..13fcfab9cae7 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -262,3 +262,4 @@ e.g.`select 2` * *History* : `history` * *Undo* : `undo` * *Redo* : `redo` + From a66db048e49c145f10e67cf58b85a585199f2eec Mon Sep 17 00:00:00 2001 From: zhuleyan <1155077028@link.cuhk.edu.hk> Date: Tue, 13 Mar 2018 16:06:23 +0800 Subject: [PATCH 11/41] add user stories --- docs/DeveloperGuide.adoc | 69 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.adoc b/docs/DeveloperGuide.adoc index 80d5c54f2ee4..ac2262907d77 100644 --- a/docs/DeveloperGuide.adoc +++ b/docs/DeveloperGuide.adoc @@ -809,9 +809,76 @@ Priorities: High (must have) - `* * \*`, Medium (nice to have) - `* \*`, Low (un |`* *` |user |hide <> 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 |clear the address book |remove all entries + +|`* * *` |user |edit a person |modify information of the person + +|`* *` |user |view all the commands that I have entered | + +|`* *` |user |undo my most recent command |change it back + +|`* *` |user |reverse the most recent undo command |regret undoing + +|`*` |user |select a person |see the Google search page of this person + +|`* * *` |user |look at my timetable | + +|`* * *` |user |add an event to my calendar | + +|`* * *` |user |delete an event from my calendar |remove events that I no longer need + +|`* * *` |user |edit an event in my calendar |modify details of an event + +|`* *` |user |get a message when an event is coming |be reminded of the event + +|`* * *` |user |add a module |record the courses I have taken + +|`* * *` |user |delete a module |remove modules that I haven't taken yet + +|`* * *` |user |edit a module |modify the course code + +|`* * *` |user |list all modules |see all courses I have taken + +|`*` |user |sort all modules by course code |locate a module easily + +|`* * *` |user |find a module by name |find it easily + +|`* * *` |user |find a module by course code |find it easily + +|`* * *` |user |find a module by semester |find it easily + +|`* * *` |user |select a module |see details of the module + +|`* *` |user |calculate my CAP | + +|`* *` |user |calculate my module credits | + +|`* *` |user |access my IVLE web page | + +|`* * *` |user |type the command in a shorter way |type it quickly + +|`* *` |user |arrange different colors for different tags |easily differentiate tags + +|`* *` |user |delete a specific tag from everyone |remove tags that I no longer need + +|`* *` |user |add a remark for a specific person |record more detailed information of the person + +|`* *` |user |edit a remark for a specific person | + +|`* *` |user |delete a remark for a specific person |remove remarks that are no longer useful + +|`*` |user |import my photo |update my profile + +|`* *` |new user |set my password |get more security + +|`* *` |user |reset my password | + +|`*` |user |change the theme of this address book |select the preferred looking + +|`*` |user |export a contact |share it with others |======================================================================= -_{More to be added}_ [appendix] == Use Cases From c67eae0249d18a81c45491f7cf06b5cd51d0ba1a Mon Sep 17 00:00:00 2001 From: zhuleyan <1155077028@link.cuhk.edu.hk> Date: Wed, 14 Mar 2018 14:37:12 +0800 Subject: [PATCH 12/41] add remark command --- .../address/logic/commands/EditCommand.java | 5 +- .../address/logic/commands/RemarkCommand.java | 111 ++++++++++++++++++ .../logic/parser/AddCommandParser.java | 5 +- .../logic/parser/AddressBookParser.java | 51 ++++---- .../seedu/address/logic/parser/CliSyntax.java | 1 + .../logic/parser/RemarkCommandParser.java | 38 ++++++ .../java/seedu/address/model/AddressBook.java | 2 +- .../seedu/address/model/person/Person.java | 7 +- .../seedu/address/model/person/Remark.java | 38 ++++++ .../address/model/util/SampleDataUtil.java | 15 ++- .../address/storage/XmlAdaptedPerson.java | 8 +- .../java/seedu/address/ui/PersonCard.java | 3 + src/main/resources/view/PersonListCard.fxml | 1 + 13 files changed, 249 insertions(+), 36 deletions(-) create mode 100644 src/main/java/seedu/address/logic/commands/RemarkCommand.java create mode 100644 src/main/java/seedu/address/logic/parser/RemarkCommandParser.java create mode 100644 src/main/java/seedu/address/model/person/Remark.java diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index e6c3a3e034bc..ed65c27d2790 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -24,6 +24,7 @@ import seedu.address.model.person.Name; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Remark; import seedu.address.model.person.exceptions.DuplicatePersonException; import seedu.address.model.person.exceptions.PersonNotFoundException; import seedu.address.model.tag.Tag; @@ -106,9 +107,10 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); + Remark updatedRemark = personToEdit.getRemark(); Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedRemark, updatedTags); } @Override @@ -194,6 +196,7 @@ public Optional
getAddress() { return Optional.ofNullable(address); } + /** * Sets {@code tags} to this object's {@code tags}. * A defensive copy of {@code tags} is used internally. diff --git a/src/main/java/seedu/address/logic/commands/RemarkCommand.java b/src/main/java/seedu/address/logic/commands/RemarkCommand.java new file mode 100644 index 000000000000..7a833f50a059 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RemarkCommand.java @@ -0,0 +1,111 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.person.Remark; +import seedu.address.model.person.Person; +import seedu.address.model.person.exceptions.DuplicatePersonException; +import seedu.address.model.person.exceptions.PersonNotFoundException; + +public class RemarkCommand extends UndoableCommand { + + public static final String COMMAND_WORD = "remark"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the remark of the person identified " + + "by the index number used in the last person listing. " + + "Existing remark will be overwritten by the input.\n" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_REMARK + "[REMARK]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_REMARK + "Likes to drink coffee."; + + public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Person: %1$s"; + public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Person: %1$s"; + + private final Index index; + private final Remark remark; + + private Person personToEdit; + private Person editedPerson; + + /** + * @param index of the person in the filtered person list to edit + * @param remark the remark to be updated to the person + */ + public RemarkCommand(Index index, Remark remark) { + requireNonNull(index); + requireNonNull(remark); + + this.index = index; + this.remark = remark; + } + + @Override + public CommandResult executeUndoableCommand() throws CommandException { + //requireNonNull(personToEdit); + //requireNonNull(editedPerson); + + try { + model.updatePerson(personToEdit, editedPerson); + } catch (DuplicatePersonException dpe) { + throw new AssertionError("The changing of the remark should not result in a duplicate"); + } catch (PersonNotFoundException pnfe) { + throw new AssertionError("The target person cannot be missing"); + } + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + @Override + protected void preprocessUndoableCommand() throws CommandException { + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + personToEdit = lastShownList.get(index.getZeroBased()); + editedPerson = new Person(personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), + personToEdit.getAddress(), remark, personToEdit.getTags()); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof RemarkCommand)) { + return false; + } + + // state check + RemarkCommand r = (RemarkCommand) other; + + return index.equals(r.index) && remark.equals(r.remark); + } + + /** + * Generate a success message according to whether the remark is added or removed + */ + private String generateSuccessMessage(Person personToEdit) { + String message; + if (remark.value.isEmpty()){ + message = MESSAGE_DELETE_REMARK_SUCCESS; + } + else { + message = MESSAGE_ADD_REMARK_SUCCESS; + } + return String.format(message, personToEdit); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index 3c729b388554..8da5db18d3db 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -6,6 +6,7 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK; import java.util.Set; import java.util.stream.Stream; @@ -18,6 +19,7 @@ import seedu.address.model.person.Name; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Remark; import seedu.address.model.tag.Tag; /** @@ -44,9 +46,10 @@ public AddCommand parse(String args) throws ParseException { Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE)).get(); Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL)).get(); Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS)).get(); + Remark remark = new Remark(""); Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - Person person = new Person(name, phone, email, address, tagList); + Person person = new Person(name, phone, email, address, remark, tagList); return new AddCommand(person); } catch (IllegalValueException ive) { diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java index b7d57f5db86a..28cb71e022ce 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java @@ -19,6 +19,7 @@ import seedu.address.logic.commands.RedoCommand; import seedu.address.logic.commands.SelectCommand; import seedu.address.logic.commands.UndoCommand; +import seedu.address.logic.commands.RemarkCommand; import seedu.address.logic.parser.exceptions.ParseException; /** @@ -47,42 +48,44 @@ public Command parseCommand(String userInput) throws ParseException { final String commandWord = matcher.group("commandWord"); final String arguments = matcher.group("arguments"); switch (commandWord) { + case AddCommand.COMMAND_WORD: + return new AddCommandParser().parse(arguments); - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); + case EditCommand.COMMAND_WORD: + return new EditCommandParser().parse(arguments); - case EditCommand.COMMAND_WORD: - return new EditCommandParser().parse(arguments); + case SelectCommand.COMMAND_WORD: + return new SelectCommandParser().parse(arguments); - case SelectCommand.COMMAND_WORD: - return new SelectCommandParser().parse(arguments); + case DeleteCommand.COMMAND_WORD: + return new DeleteCommandParser().parse(arguments); - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); + case ClearCommand.COMMAND_WORD: + return new ClearCommand(); - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); + case FindCommand.COMMAND_WORD: + return new FindCommandParser().parse(arguments); - case FindCommand.COMMAND_WORD: - return new FindCommandParser().parse(arguments); + case ListCommand.COMMAND_WORD: + return new ListCommand(); - case ListCommand.COMMAND_WORD: - return new ListCommand(); + case HistoryCommand.COMMAND_WORD: + return new HistoryCommand(); - case HistoryCommand.COMMAND_WORD: - return new HistoryCommand(); + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); - case ExitCommand.COMMAND_WORD: - return new ExitCommand(); + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); - case HelpCommand.COMMAND_WORD: - return new HelpCommand(); + case UndoCommand.COMMAND_WORD: + return new UndoCommand(); - case UndoCommand.COMMAND_WORD: - return new UndoCommand(); + case RedoCommand.COMMAND_WORD: + return new RedoCommand(); - case RedoCommand.COMMAND_WORD: - return new RedoCommand(); + case RemarkCommand.COMMAND_WORD: + return new RemarkCommandParser().parse(arguments); default: throw new ParseException(MESSAGE_UNKNOWN_COMMAND); diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 75b1a9bf1190..96be13b37720 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -11,5 +11,6 @@ public class CliSyntax { public static final Prefix PREFIX_EMAIL = new Prefix("e/"); public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); + public static final Prefix PREFIX_REMARK = new Prefix("r/"); } diff --git a/src/main/java/seedu/address/logic/parser/RemarkCommandParser.java b/src/main/java/seedu/address/logic/parser/RemarkCommandParser.java new file mode 100644 index 000000000000..1a6785861aea --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/RemarkCommandParser.java @@ -0,0 +1,38 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK; + +import seedu.address.commons.core.index.Index; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.logic.commands.RemarkCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.Remark; + + +public class RemarkCommandParser implements Parser{ + + /** + * Parses the given {@code String} of arguments in the context of the RemarkCommand + * and returns a RemarkCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public RemarkCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_REMARK); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (IllegalValueException ive) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, RemarkCommand.MESSAGE_USAGE)); + } + + + String remark = argMultimap.getValue(PREFIX_REMARK).orElse(""); + + return new RemarkCommand(index, new Remark(remark)); + } +} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 7251d520bbaf..dc2b04970c61 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -132,7 +132,7 @@ private Person syncWithMasterTagList(Person person) { final Set correctTagReferences = new HashSet<>(); personTags.forEach(tag -> correctTagReferences.add(masterTagObjects.get(tag))); return new Person( - person.getName(), person.getPhone(), person.getEmail(), person.getAddress(), correctTagReferences); + person.getName(), person.getPhone(), person.getEmail(), person.getAddress(), person.getRemark(), correctTagReferences); } /** diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index ec9f2aa5e919..63c420119641 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -19,18 +19,19 @@ public class Person { private final Phone phone; private final Email email; private final Address address; - + private final Remark remark; private final UniqueTagList tags; /** * Every field must be present and not null. */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { + public Person(Name name, Phone phone, Email email, Address address, Remark remark, Set tags) { requireAllNonNull(name, phone, email, address, tags); this.name = name; this.phone = phone; this.email = email; this.address = address; + this.remark = remark; // protect internal tags from changes in the arg list this.tags = new UniqueTagList(tags); } @@ -51,6 +52,8 @@ public Address getAddress() { return address; } + public Remark getRemark() { return remark; } + /** * Returns an immutable tag set, which throws {@code UnsupportedOperationException} * if modification is attempted. diff --git a/src/main/java/seedu/address/model/person/Remark.java b/src/main/java/seedu/address/model/person/Remark.java new file mode 100644 index 000000000000..2b236fb5d8bf --- /dev/null +++ b/src/main/java/seedu/address/model/person/Remark.java @@ -0,0 +1,38 @@ +package seedu.address.model.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Person's remark in the address book. + * Guarantees: immutable; is valid + */ +public class Remark { + + public static final String MESSAGE_PHONE_CONSTRAINTS = + "Person remarks can take any values, can even be blank"; + public final String value; + + public Remark(String remark) { + //requireNonNull(remark); + this.value = remark; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Remark // instanceof handles nulls + && this.value.equals(((Remark) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 31d4034a3845..ddbde903d494 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -11,6 +11,7 @@ import seedu.address.model.person.Name; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Remark; import seedu.address.model.person.exceptions.DuplicatePersonException; import seedu.address.model.tag.Tag; @@ -18,25 +19,27 @@ * Contains utility methods for populating {@code AddressBook} with sample data. */ public class SampleDataUtil { + + public static final Remark NO_REMARK = new Remark(""); public static Person[] getSamplePersons() { return new Person[] { new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), + new Address("Blk 30 Geylang Street 29, #06-40"), NO_REMARK, getTagSet("friends")), new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), + new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), NO_REMARK, getTagSet("colleagues", "friends")), new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), + new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), NO_REMARK, getTagSet("neighbours")), new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), + new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), NO_REMARK, getTagSet("family")), new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), + new Address("Blk 47 Tampines Street 20, #17-35"), NO_REMARK, getTagSet("classmates")), new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), + new Address("Blk 45 Aljunied Street 85, #11-31"), NO_REMARK, getTagSet("colleagues")) }; } diff --git a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java b/src/main/java/seedu/address/storage/XmlAdaptedPerson.java index 91a57b0380d4..e067b9e74dcd 100644 --- a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java +++ b/src/main/java/seedu/address/storage/XmlAdaptedPerson.java @@ -15,6 +15,7 @@ import seedu.address.model.person.Name; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; +import seedu.address.model.person.Remark; import seedu.address.model.tag.Tag; /** @@ -32,6 +33,8 @@ public class XmlAdaptedPerson { private String email; @XmlElement(required = true) private String address; + @XmlElement(required = true) + private String remark; @XmlElement private List tagged = new ArrayList<>(); @@ -65,6 +68,7 @@ public XmlAdaptedPerson(Person source) { phone = source.getPhone().value; email = source.getEmail().value; address = source.getAddress().value; + remark = source.getRemark().value; tagged = source.getTags().stream() .map(XmlAdaptedTag::new) .collect(Collectors.toList()); @@ -113,8 +117,10 @@ public Person toModelType() throws IllegalValueException { } final Address address = new Address(this.address); + final Remark remark = new Remark(this.remark); + final Set tags = new HashSet<>(personTags); - return new Person(name, phone, email, address, tags); + return new Person(name, phone, email, address, remark, tags); } @Override diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java index f6727ea83abd..b411bf329ed5 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/address/ui/PersonCard.java @@ -37,6 +37,8 @@ public class PersonCard extends UiPart { @FXML private Label email; @FXML + private Label remark; + @FXML private FlowPane tags; public PersonCard(Person person, int displayedIndex) { @@ -47,6 +49,7 @@ public PersonCard(Person person, int displayedIndex) { phone.setText(person.getPhone().value); address.setText(person.getAddress().value); email.setText(person.getEmail().value); + remark.setText(person.getRemark().value); person.getTags().forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); } diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml index f08ea32ad558..d1a7eb614f83 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/PersonListCard.fxml @@ -31,6 +31,7 @@