From 41a5b2d5c156fa853b3ee99804b15be40e1f4a73 Mon Sep 17 00:00:00 2001 From: VietAnh1010 Date: Wed, 22 Feb 2023 19:39:11 +0800 Subject: [PATCH 001/470] VSCode: Add new configurations --- .gitignore | 3 + .vscode/eclipse-java-google-style.xml | 337 ++++++++++++++++++++++++++ .vscode/settings.json | 25 ++ .vscode/vscode-java.prefs | 1 + build.gradle | 1 - 5 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 .vscode/eclipse-java-google-style.xml create mode 100644 .vscode/settings.json create mode 100644 .vscode/vscode-java.prefs diff --git a/.gitignore b/.gitignore index 71c9194e8bd..82c8f5dab6a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ src/main/resources/docs/ /out/ /*.iml +# VSCode files +/bin/ + # Storage/log files /data/ /config.json diff --git a/.vscode/eclipse-java-google-style.xml b/.vscode/eclipse-java-google-style.xml new file mode 100644 index 00000000000..c034f89f2c2 --- /dev/null +++ b/.vscode/eclipse-java-google-style.xml @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..c9eda2d29a2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,25 @@ +{ + "java.completion.importOrder": [ + "#", + "java", + "javax", + "org", + "com", + "" + ], + "java.settings.url": ".vscode/vscode-java.prefs", + "java.format.settings.url": ".vscode/eclipse-java-google-style.xml", + "java.project.sourcePaths": ["src/main/java", "src/test/java", ""], + "java.configuration.updateBuildConfiguration": "interactive", + "[java]": { + "editor.detectIndentation": false, + "editor.rulers": [100], + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + "editor.trimAutoWhitespace": true + }, + "files.trimTrailingWhitespace": true, + "java.inlayHints.parameterNames.enabled": "none", + /* enable if you want to see VSCode suggestions */ + "css.validate": false +} diff --git a/.vscode/vscode-java.prefs b/.vscode/vscode-java.prefs new file mode 100644 index 00000000000..cb49392820c --- /dev/null +++ b/.vscode/vscode-java.prefs @@ -0,0 +1 @@ +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore diff --git a/build.gradle b/build.gradle index 108397716bd..7b33adc2f17 100644 --- a/build.gradle +++ b/build.gradle @@ -61,7 +61,6 @@ dependencies { implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: jUnitVersion - testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: jUnitVersion } From c66d0ccd4ac9ae17302c6756e9b0eeb2e06dac78 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 22 Feb 2023 20:44:00 +0800 Subject: [PATCH 002/470] Implement basic UI layout --- src/main/java/seedu/address/Main.java | 19 ++--- src/main/java/seedu/address/MainUi.java | 109 ++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 src/main/java/seedu/address/MainUi.java diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/address/Main.java index 052a5068631..f159c411084 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/address/Main.java @@ -5,21 +5,20 @@ /** * The main entry point to the application. * - * This is a workaround for the following error when MainApp is made the - * entry point of the application: + * This is a workaround for the following error when MainApp is made the entry point of the + * application: * - * Error: JavaFX runtime components are missing, and are required to run this application + * Error: JavaFX runtime components are missing, and are required to run this application * - * The reason is that MainApp extends Application. In that case, the - * LauncherHelper will check for the javafx.graphics module to be present - * as a named module. We don't use JavaFX via the module system so it can't - * find the javafx.graphics module, and so the launch is aborted. + * The reason is that MainApp extends Application. In that case, the LauncherHelper will check for + * the javafx.graphics module to be present as a named module. We don't use JavaFX via the module + * system so it can't find the javafx.graphics module, and so the launch is aborted. * - * By having a separate main class (Main) that doesn't extend Application - * to be the entry point of the application, we avoid this issue. + * By having a separate main class (Main) that doesn't extend Application to be the entry point of + * the application, we avoid this issue. */ public class Main { public static void main(String[] args) { - Application.launch(MainApp.class, args); + Application.launch(MainUi.class, args); } } diff --git a/src/main/java/seedu/address/MainUi.java b/src/main/java/seedu/address/MainUi.java new file mode 100644 index 00000000000..256679368ed --- /dev/null +++ b/src/main/java/seedu/address/MainUi.java @@ -0,0 +1,109 @@ +package seedu.address; + +import javafx.application.Application; +import javafx.application.Platform; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.Border; +import javafx.scene.layout.BorderStroke; +import javafx.scene.layout.BorderStrokeStyle; +import javafx.scene.layout.BorderWidths; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.stage.Stage; + +public class MainUi extends Application { + private static BorderStroke borderStroke = + new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, null, new BorderWidths(2)); + private static Border border = new Border(borderStroke); + + + private static double MAIN_WINDOW_WIDTH = 400; + private static double MAIN_WINDOW_HEIGHT = 400; + + @Override + public void start(Stage stage) { + // Create the main screen components + Label mainLabel = new Label("Main Screen"); + + // TODO : Refactor this into a custom component + VBox leftComponent = new VBox(new Label("Task List Component")); + + // TODO : Refactor this into a fxml or make it more elegant + leftComponent.prefHeightProperty().bind(stage.heightProperty().multiply(0.9)); + leftComponent.prefWidthProperty().bind(stage.widthProperty().multiply(0.3)); + leftComponent.setBorder(border); + + // TODO : Refactor this into a custom component + VBox rightComponent = new VBox(new Label("Detailed Task Component")); + + // TODO : Refactor this into a fxml or make it more elegant + rightComponent.prefHeightProperty().bind(stage.heightProperty().multiply(0.9)); + rightComponent.prefWidthProperty().bind(stage.widthProperty().multiply(0.7)); + rightComponent.setBorder(border); + + HBox mainComponents = new HBox(leftComponent, rightComponent); + VBox mainScreen = new VBox(mainLabel, mainComponents); + + // Create the text box component + TextField textBox = new TextField(); + VBox textBoxComponent = new VBox(textBox); + + // TODO : Refactor this into a fxml or make it more elegant + textBoxComponent.prefHeightProperty().bind(stage.heightProperty().multiply(0.1)); + textBox.prefHeightProperty().bind(textBoxComponent.heightProperty().multiply(1)); + textBoxComponent.setVisible(false); + + + // TODO : Refactor each event listener to obey SLAP + // Set up the ":" key listener to show/hide the text box component + mainScreen.setOnKeyPressed(event -> { + if (event.getText().equals(":")) { + textBoxComponent.setVisible(true); + textBox.requestFocus(); + } + }); + + textBoxComponent.setOnKeyPressed(event -> { + if (event.getCode().equals(KeyCode.ESCAPE)) { + textBoxComponent.setVisible(false); + mainScreen.requestFocus(); + System.out.println("You escaped"); + } + }); + + textBox.setOnKeyPressed(event -> { + if (event.getCode().equals(KeyCode.ENTER)) { + textBoxComponent.setVisible(false); + mainScreen.requestFocus(); + String commandString = textBox.getText(); + System.out.println("Your command is " + commandString); + + // TODO : Create a Parser to parse the command and create a Driver to run it. + if (commandString.equals(":wq!")) { + Platform.exit(); + } + } + }); + + + // Set up the layout + VBox root = new VBox(mainScreen, textBoxComponent); + root.setAlignment(Pos.CENTER); + Scene scene = new Scene(root, MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT); + + root.prefWidthProperty().bind(stage.widthProperty()); + root.prefHeightProperty().bind(stage.heightProperty()); + + // Show the window + stage.setScene(scene); + stage.show(); + mainScreen.requestFocus(); + + } + +} From 46e2d1a2cbdedd7b2612338177a3519729475e66 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 3 Mar 2023 22:43:48 +0800 Subject: [PATCH 003/470] Update profile pic --- docs/images/bipbipboopboop.png | Bin 0 -> 14531 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/bipbipboopboop.png diff --git a/docs/images/bipbipboopboop.png b/docs/images/bipbipboopboop.png new file mode 100644 index 0000000000000000000000000000000000000000..bd192ccf4106b3174de6054c308cc6808880d6ae GIT binary patch literal 14531 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 CRNX@W literal 0 HcmV?d00001 From 8135a848351502762b735d7afa799a99d52d1df1 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 3 Mar 2023 22:52:00 +0800 Subject: [PATCH 004/470] Update AboutUs.md and add personal portfolio --- docs/AboutUs.md | 24 ++++++++++++------------ docs/team/bipbipboopboop.md | 0 2 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 docs/team/bipbipboopboop.md diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..8a0ffc89d37 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -17,7 +17,7 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` [[github](https://github.com/johndoe)] [[portfolio](team/johndoe.md)] -* Role: Project Advisor +- Role: Project Advisor ### Jane Doe @@ -26,17 +26,17 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` [[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] -* Role: Team Lead -* Responsibilities: UI +- Role: Team Lead +- Responsibilities: UI -### Johnny Doe +### Ethan Chen Ee Shuen - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/bipbipboopboop)] [[portfolio](team/bipbipboopboop.md)] -* Role: Developer -* Responsibilities: Data +- Role: Developer +- Responsibilities: UI + Integration ### Jean Doe @@ -45,8 +45,8 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` [[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] -* Role: Developer -* Responsibilities: Dev Ops + Threading +- Role: Developer +- Responsibilities: Dev Ops + Threading ### James Doe @@ -55,5 +55,5 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` [[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] -* Role: Developer -* Responsibilities: UI +- Role: Developer +- Responsibilities: UI diff --git a/docs/team/bipbipboopboop.md b/docs/team/bipbipboopboop.md new file mode 100644 index 00000000000..e69de29bb2d From a594b78940a3e6130bf21ea6ba5dfdaa1cc263f3 Mon Sep 17 00:00:00 2001 From: VietAnh1010 Date: Tue, 7 Mar 2023 14:49:02 +0800 Subject: [PATCH 005/470] VietAnh1010: Update personal information --- docs/AboutUs.md | 24 ++++++++++++------------ docs/images/vietanh1010.png | Bin 0 -> 1961 bytes docs/team/vietanh1010.md | 8 ++++++++ 3 files changed, 20 insertions(+), 12 deletions(-) create mode 100644 docs/images/vietanh1010.png create mode 100644 docs/team/vietanh1010.md diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..46822d460f6 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,21 +5,21 @@ title: About Us We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). -You can reach us at the email `seer[at]comp.nus.edu.sg` +You can reach us at the email `what email should we put here?` -## Project team +# Project team -### John Doe +## Nguyen Viet Anh - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/VietAnh1010)] +[[portfolio](team/vietanh1010.md)] -* Role: Project Advisor +* Role: Team lead, Integration, Java expert +* Responsibilities: Parser, Model -### Jane Doe +## Jane Doe @@ -29,7 +29,7 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` * Role: Team Lead * Responsibilities: UI -### Johnny Doe +## Johnny Doe @@ -38,7 +38,7 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` * Role: Developer * Responsibilities: Data -### Jean Doe +## Jean Doe @@ -48,7 +48,7 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` * Role: Developer * Responsibilities: Dev Ops + Threading -### James Doe +## James Doe diff --git a/docs/images/vietanh1010.png b/docs/images/vietanh1010.png new file mode 100644 index 0000000000000000000000000000000000000000..1ce7ce16dc8717e182da5f82fe0f7ee9c34137bf GIT binary patch literal 1961 zcmb_deN<9+7`~;Z35JG^)TU((oNJ2A)YREBP%I+UW}kiwI`WWHBi#lhmd+GCe#_84 z{HUR6Xk}rkX^V^$GabqNDs@i$C=)|P)LnC)`eWy8=WO@h=Y8+{ywC6Vo_n8j&rKo` z-Su>hbpZg-^T1=hz$^oRRe+8L7@1q(aUiiO%FEpqsE}-ugN0`3?)|#~pfU}j4%7nc z&q;)XxXH;$g+lT6?c3?;>Dk%Yxw$#DT0K8Mzp${dxVShoGcz?crBo_aDiyfmyVX)2 zm|)RfL~pSB!2xu1=UoO{Ya{SSq5uG4u!L1&q+%BUtQqmZ?migfH{R72+2sJ$dRnJk zWZm(J9_zFv8?49f`}o4+@Mf$hs$IxKC z6Fb!Y)#@<`m;2070TX`)-PJbJGz=++K|iuZ7=2$<`%8z(xK@HC6r7DFg*c2 zz{yNY=VZdxr)Dgj>)^2UD_E)0(tGZ+_1i$Pezz!<5_C-V3?Jt7{Zaf_@N>(7IoWhv z!?qkNbe4F8Jrt-qzEh%JK#hFuW}0QP_Unx?qzbe^G{sahf-s2%$tfk3)e?ev@(Dt5 zm8yVgb+=EBx^b_V*P_gz*>&x!k%e2!DK~2Wkzi~*+QX#OF!Uavp${U^tZnMc(faNe z4r5vMtx7J(^8Ov361zquN|jYB^K->kSlLV2*xKyn+Gdzi?B;xgjCHI zFfGgaQzz?qa z11!k2zZwx#mx~iwJ#6Rwc>IiZ;*S@&xwL?Wov{OSWq!(|@eo;3%;D9L1C2#hohn9e zW>delA}=I|m7ciie%#47=l3|g`M5Vi-@Wd>#=1e$$qF+uzpmRQFW7e{1O4c`$k_k9gd0DB2oqEc7555=IwjJCW> zKUI4jeIOj%dEnK{{L#n1$M2Npb|prZ+CPU4l31_IMBI}T?0ARCPfWiBwCf%@Rmm27 zdo#o%*A@A;(hF=<2C9hAq?oP>~x z7nC5xi-z)ItA Date: Tue, 7 Mar 2023 14:53:05 +0800 Subject: [PATCH 006/470] vietanh1010.md: Fix missing EOL error --- docs/team/vietanh1010.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/team/vietanh1010.md b/docs/team/vietanh1010.md index 0c1e5e61f3d..96330538d9d 100644 --- a/docs/team/vietanh1010.md +++ b/docs/team/vietanh1010.md @@ -5,4 +5,4 @@ title: Nguyen Viet Anh's Project Portfolio Page # Project: Vimification -More details will be added soon... \ No newline at end of file +More details will be added soon... From 3f4d8e0397dd318ea67df300d8973c0a27279c32 Mon Sep 17 00:00:00 2001 From: VietAnh1010 Date: Tue, 7 Mar 2023 15:05:47 +0800 Subject: [PATCH 007/470] Implement skeleton version of remark command --- .../seedu/address/logic/commands/RemarkCommand.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/java/seedu/address/logic/commands/RemarkCommand.java 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 00000000000..ad3d2cf931e --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/RemarkCommand.java @@ -0,0 +1,13 @@ +package seedu.address.logic.commands; + +import seedu.address.model.Model; + +public class RemarkCommand extends Command { + + public static String COMMAND_WORD = "remark"; + + @Override + public CommandResult execute(Model model) { + return new CommandResult("Hello from remark"); + } +} From c24064bb6668918031625539b09e5f091507f01e Mon Sep 17 00:00:00 2001 From: VietAnh1010 Date: Tue, 7 Mar 2023 23:31:48 +0800 Subject: [PATCH 008/470] Rename package from seedu.address to vimification The old package name is seedu.address. We need to change the name to make it clear that this is the code base for Vimification. Also, changing the package name removes 1 level of directories. The extra level of nested directory is not necessary. The package is renamed from seedu.address to vimification. Occurrences of seedu.address in all files are also replaced with vimification. This is a big change that affects the entire codebase. Testing is required to ensure that the code still works correctly after the renaming. --- build.gradle | 2 +- docs/DeveloperGuide.md | 2 +- docs/SettingUp.md | 2 +- docs/Testing.md | 6 +- docs/tutorials/AddRemark.md | 12 +- docs/tutorials/RemovingFields.md | 2 +- docs/tutorials/TracingCode.md | 4 +- .../address/logic/commands/AddCommand.java | 67 --- .../seedu/address/model/person/Email.java | 71 --- .../exceptions/PersonNotFoundException.java | 6 - .../address/model/util/SampleDataUtil.java | 60 -- .../AppParameters.java | 12 +- .../{seedu/address => vimification}/Main.java | 2 +- .../address => vimification}/MainApp.java | 92 ++-- .../address => vimification}/MainUi.java | 2 +- .../commons/core/Config.java | 4 +- .../commons/core/GuiSettings.java | 7 +- .../commons/core/LogsCenter.java | 24 +- .../commons/core/Messages.java | 5 +- .../commons/core/Version.java | 6 +- .../commons/core/index/Index.java | 13 +- .../exceptions/DataConversionException.java | 2 +- .../exceptions/IllegalValueException.java | 2 +- .../commons/util/AppUtil.java | 4 +- .../commons/util/CollectionUtil.java | 2 +- .../commons/util/ConfigUtil.java | 6 +- .../commons/util/FileUtil.java | 11 +- .../commons/util/JsonUtil.java | 25 +- .../commons/util/StringUtil.java | 30 +- .../address => vimification}/logic/Logic.java | 17 +- .../logic/LogicManager.java | 24 +- .../logic/commands/AddCommand.java | 69 +++ .../logic/commands/ClearCommand.java | 6 +- .../logic/commands/Command.java | 6 +- .../logic/commands/CommandResult.java | 6 +- .../logic/commands/DeleteCommand.java | 14 +- .../logic/commands/EditCommand.java | 89 +-- .../logic/commands/ExitCommand.java | 7 +- .../logic/commands/FindCommand.java | 16 +- .../logic/commands/HelpCommand.java | 9 +- .../logic/commands/ListCommand.java | 6 +- .../logic/commands/RemarkCommand.java | 4 +- .../commands/exceptions/CommandException.java | 5 +- .../logic/parser/AddCommandParser.java | 50 +- .../logic/parser/AddressBookParser.java | 32 +- .../logic/parser/ApplicativeParser.java | 521 ++++++++++++++++++ .../logic/parser/ArgumentMultimap.java | 26 +- .../logic/parser/ArgumentTokenizer.java | 79 +-- .../logic/parser/CliSyntax.java | 2 +- .../logic/parser/DeleteCommandParser.java | 15 +- .../logic/parser/EditCommandParser.java | 53 +- .../logic/parser/FindCommandParser.java | 15 +- .../logic/parser/Parser.java | 7 +- .../logic/parser/ParserException.java | 21 + .../logic/parser/ParserUtil.java | 43 +- .../logic/parser/Prefix.java | 6 +- .../parser/exceptions/ParseException.java | 4 +- .../model/AddressBook.java | 35 +- .../address => vimification}/model/Model.java | 19 +- .../model/ModelManager.java | 22 +- .../model/ReadOnlyAddressBook.java | 8 +- .../model/ReadOnlyUserPrefs.java | 4 +- .../model/UserPrefs.java | 8 +- .../model/person/Address.java | 17 +- .../java/vimification/model/person/Email.java | 79 +++ .../model/person/Name.java | 14 +- .../person/NameContainsKeywordsPredicate.java | 10 +- .../model/person/Person.java | 22 +- .../model/person/Phone.java | 10 +- .../model/person/UniquePersonList.java | 35 +- .../exceptions/DuplicatePersonException.java | 6 +- .../exceptions/PersonNotFoundException.java | 7 + .../model/tag/Tag.java | 10 +- .../model/util/SampleDataUtil.java | 66 +++ .../storage/AddressBookStorage.java | 17 +- .../storage/JsonAdaptedPerson.java | 31 +- .../storage/JsonAdaptedTag.java | 6 +- .../storage/JsonAddressBookStorage.java | 17 +- .../storage/JsonSerializableAddressBook.java | 19 +- .../storage/JsonUserPrefsStorage.java | 11 +- .../storage/Storage.java | 10 +- .../storage/StorageManager.java | 24 +- .../storage/UserPrefsStorage.java | 18 +- .../ui/CommandBox.java | 13 +- .../ui/HelpWindow.java | 27 +- .../ui/MainWindow.java | 41 +- .../ui/PersonCard.java | 13 +- .../ui/PersonListPanel.java | 9 +- .../ui/ResultDisplay.java | 2 +- .../ui/StatusBarFooter.java | 2 +- .../address => vimification}/ui/Ui.java | 4 +- .../ui/UiManager.java | 30 +- .../address => vimification}/ui/UiPart.java | 22 +- .../logic/commands/ExitCommandTest.java | 20 - .../logic/parser/AddCommandParserTest.java | 141 ----- .../logic/parser/DeleteCommandParserTest.java | 32 -- .../address/testutil/TypicalPersons.java | 76 --- .../AppParametersTest.java | 2 +- .../commons/core/ConfigTest.java | 2 +- .../commons/core/VersionTest.java | 16 +- .../commons/core/index/IndexTest.java | 4 +- .../commons/util/AppUtilTest.java | 7 +- .../commons/util/CollectionUtilTest.java | 15 +- .../commons/util/ConfigUtilTest.java | 19 +- .../commons/util/FileUtilTest.java | 4 +- .../commons/util/JsonUtilTest.java | 22 +- .../commons/util/StringUtilTest.java | 77 ++- .../logic/LogicManagerTest.java | 89 +-- .../commands/AddCommandIntegrationTest.java | 21 +- .../logic/commands/AddCommandTest.java | 26 +- .../logic/commands/ClearCommandTest.java | 20 +- .../logic/commands/CommandResultTest.java | 8 +- .../logic/commands/CommandTestUtil.java | 69 ++- .../logic/commands/DeleteCommandTest.java | 41 +- .../logic/commands/EditCommandTest.java | 84 +-- .../commands/EditPersonDescriptorTest.java | 26 +- .../logic/commands/ExitCommandTest.java | 21 + .../logic/commands/FindCommandTest.java | 22 +- .../logic/commands/HelpCommandTest.java | 10 +- .../logic/commands/ListCommandTest.java | 16 +- .../logic/parser/AddCommandParserTest.java | 167 ++++++ .../logic/parser/AddressBookParserTest.java | 54 +- .../logic/parser/ArgumentTokenizerTest.java | 16 +- .../logic/parser/CommandParserTestUtil.java | 17 +- .../logic/parser/DeleteCommandParserTest.java | 32 ++ .../logic/parser/EditCommandParserTest.java | 158 +++--- .../logic/parser/FindCommandParserTest.java | 15 +- .../logic/parser/ParserUtilTest.java | 30 +- .../model/AddressBookTest.java | 31 +- .../model/ModelManagerTest.java | 25 +- .../model/UserPrefsTest.java | 4 +- .../model/person/AddressTest.java | 8 +- .../model/person/EmailTest.java | 28 +- .../NameContainsKeywordsPredicateTest.java | 22 +- .../model/person/NameTest.java | 4 +- .../model/person/PersonTest.java | 25 +- .../model/person/PhoneTest.java | 4 +- .../model/person/UniquePersonListTest.java | 41 +- .../model/tag/TagTest.java | 4 +- .../storage/JsonAdaptedPersonTest.java | 57 +- .../storage/JsonAddressBookStorageTest.java | 36 +- .../JsonSerializableAddressBookTest.java | 27 +- .../storage/JsonUserPrefsStorageTest.java | 29 +- .../storage/StorageManagerTest.java | 27 +- .../testutil/AddressBookBuilder.java | 11 +- .../testutil/Assert.java | 19 +- .../testutil/EditPersonDescriptorBuilder.java | 20 +- .../testutil/PersonBuilder.java | 21 +- .../testutil/PersonUtil.java | 35 +- .../testutil/SerializableTestClass.java | 2 +- .../testutil/TestUtil.java | 12 +- .../testutil/TypicalIndexes.java | 4 +- .../vimification/testutil/TypicalPersons.java | 88 +++ .../ui/TestFxmlObject.java | 6 +- .../ui/UiPartTest.java | 31 +- .../resources/view/UiPartTest/validFile.fxml | 2 +- .../view/UiPartTest/validFileWithFxRoot.fxml | 2 +- 157 files changed, 2591 insertions(+), 1736 deletions(-) delete mode 100644 src/main/java/seedu/address/logic/commands/AddCommand.java delete mode 100644 src/main/java/seedu/address/model/person/Email.java delete mode 100644 src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java delete mode 100644 src/main/java/seedu/address/model/util/SampleDataUtil.java rename src/main/java/{seedu/address => vimification}/AppParameters.java (81%) rename src/main/java/{seedu/address => vimification}/Main.java (97%) rename src/main/java/{seedu/address => vimification}/MainApp.java (65%) rename src/main/java/{seedu/address => vimification}/MainUi.java (99%) rename src/main/java/{seedu/address => vimification}/commons/core/Config.java (93%) rename src/main/java/{seedu/address => vimification}/commons/core/GuiSettings.java (91%) rename src/main/java/{seedu/address => vimification}/commons/core/LogsCenter.java (83%) rename src/main/java/{seedu/address => vimification}/commons/core/Messages.java (80%) rename src/main/java/{seedu/address => vimification}/commons/core/Version.java (94%) rename src/main/java/{seedu/address => vimification}/commons/core/index/Index.java (70%) rename src/main/java/{seedu/address => vimification}/commons/exceptions/DataConversionException.java (84%) rename src/main/java/{seedu/address => vimification}/commons/exceptions/IllegalValueException.java (93%) rename src/main/java/{seedu/address => vimification}/commons/util/AppUtil.java (94%) rename src/main/java/{seedu/address => vimification}/commons/util/CollectionUtil.java (96%) rename src/main/java/{seedu/address => vimification}/commons/util/ConfigUtil.java (77%) rename src/main/java/{seedu/address => vimification}/commons/util/FileUtil.java (90%) rename src/main/java/{seedu/address => vimification}/commons/util/JsonUtil.java (89%) rename src/main/java/{seedu/address => vimification}/commons/util/StringUtil.java (72%) rename src/main/java/{seedu/address => vimification}/logic/Logic.java (73%) rename src/main/java/{seedu/address => vimification}/logic/LogicManager.java (77%) create mode 100644 src/main/java/vimification/logic/commands/AddCommand.java rename src/main/java/{seedu/address => vimification}/logic/commands/ClearCommand.java (80%) rename src/main/java/{seedu/address => vimification}/logic/commands/Command.java (78%) rename src/main/java/{seedu/address => vimification}/logic/commands/CommandResult.java (93%) rename src/main/java/{seedu/address => vimification}/logic/commands/DeleteCommand.java (80%) rename src/main/java/{seedu/address => vimification}/logic/commands/EditCommand.java (70%) rename src/main/java/{seedu/address => vimification}/logic/commands/ExitCommand.java (60%) rename src/main/java/{seedu/address => vimification}/logic/commands/FindCommand.java (71%) rename src/main/java/{seedu/address => vimification}/logic/commands/HelpCommand.java (62%) rename src/main/java/{seedu/address => vimification}/logic/commands/ListCommand.java (78%) rename src/main/java/{seedu/address => vimification}/logic/commands/RemarkCommand.java (75%) rename src/main/java/{seedu/address => vimification}/logic/commands/exceptions/CommandException.java (79%) rename src/main/java/{seedu/address => vimification}/logic/parser/AddCommandParser.java (52%) rename src/main/java/{seedu/address => vimification}/logic/parser/AddressBookParser.java (63%) create mode 100644 src/main/java/vimification/logic/parser/ApplicativeParser.java rename src/main/java/{seedu/address => vimification}/logic/parser/ArgumentMultimap.java (58%) rename src/main/java/{seedu/address => vimification}/logic/parser/ArgumentTokenizer.java (64%) rename src/main/java/{seedu/address => vimification}/logic/parser/CliSyntax.java (92%) rename src/main/java/{seedu/address => vimification}/logic/parser/DeleteCommandParser.java (65%) rename src/main/java/{seedu/address => vimification}/logic/parser/EditCommandParser.java (52%) rename src/main/java/{seedu/address => vimification}/logic/parser/FindCommandParser.java (67%) rename src/main/java/{seedu/address => vimification}/logic/parser/Parser.java (71%) create mode 100644 src/main/java/vimification/logic/parser/ParserException.java rename src/main/java/{seedu/address => vimification}/logic/parser/ParserUtil.java (76%) rename src/main/java/{seedu/address => vimification}/logic/parser/Prefix.java (89%) rename src/main/java/{seedu/address => vimification}/logic/parser/exceptions/ParseException.java (73%) rename src/main/java/{seedu/address => vimification}/model/AddressBook.java (71%) rename src/main/java/{seedu/address => vimification}/model/Model.java (80%) rename src/main/java/{seedu/address => vimification}/model/ModelManager.java (84%) rename src/main/java/{seedu/address => vimification}/model/ReadOnlyAddressBook.java (51%) rename src/main/java/{seedu/address => vimification}/model/ReadOnlyUserPrefs.java (70%) rename src/main/java/{seedu/address => vimification}/model/UserPrefs.java (90%) rename src/main/java/{seedu/address => vimification}/model/person/Address.java (67%) create mode 100644 src/main/java/vimification/model/person/Email.java rename src/main/java/{seedu/address => vimification}/model/person/Name.java (76%) rename src/main/java/{seedu/address => vimification}/model/person/NameContainsKeywordsPredicate.java (67%) rename src/main/java/{seedu/address => vimification}/model/person/Person.java (83%) rename src/main/java/{seedu/address => vimification}/model/person/Phone.java (78%) rename src/main/java/{seedu/address => vimification}/model/person/UniquePersonList.java (73%) rename src/main/java/{seedu/address => vimification}/model/person/exceptions/DuplicatePersonException.java (67%) create mode 100644 src/main/java/vimification/model/person/exceptions/PersonNotFoundException.java rename src/main/java/{seedu/address => vimification}/model/tag/Tag.java (78%) create mode 100644 src/main/java/vimification/model/util/SampleDataUtil.java rename src/main/java/{seedu/address => vimification}/storage/AddressBookStorage.java (76%) rename src/main/java/{seedu/address => vimification}/storage/JsonAdaptedPerson.java (76%) rename src/main/java/{seedu/address => vimification}/storage/JsonAdaptedTag.java (89%) rename src/main/java/{seedu/address => vimification}/storage/JsonAddressBookStorage.java (85%) rename src/main/java/{seedu/address => vimification}/storage/JsonSerializableAddressBook.java (78%) rename src/main/java/{seedu/address => vimification}/storage/JsonUserPrefsStorage.java (82%) rename src/main/java/{seedu/address => vimification}/storage/Storage.java (73%) rename src/main/java/{seedu/address => vimification}/storage/StorageManager.java (79%) rename src/main/java/{seedu/address => vimification}/storage/UserPrefsStorage.java (61%) rename src/main/java/{seedu/address => vimification}/ui/CommandBox.java (85%) rename src/main/java/{seedu/address => vimification}/ui/HelpWindow.java (75%) rename src/main/java/{seedu/address => vimification}/ui/MainWindow.java (82%) rename src/main/java/{seedu/address => vimification}/ui/PersonCard.java (86%) rename src/main/java/{seedu/address => vimification}/ui/PersonListPanel.java (89%) rename src/main/java/{seedu/address => vimification}/ui/ResultDisplay.java (95%) rename src/main/java/{seedu/address => vimification}/ui/StatusBarFooter.java (96%) rename src/main/java/{seedu/address => vimification}/ui/Ui.java (64%) rename src/main/java/{seedu/address => vimification}/ui/UiManager.java (78%) rename src/main/java/{seedu/address => vimification}/ui/UiPart.java (83%) delete mode 100644 src/test/java/seedu/address/logic/commands/ExitCommandTest.java delete mode 100644 src/test/java/seedu/address/logic/parser/AddCommandParserTest.java delete mode 100644 src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java delete mode 100644 src/test/java/seedu/address/testutil/TypicalPersons.java rename src/test/java/{seedu/address => vimification}/AppParametersTest.java (98%) rename src/test/java/{seedu/address => vimification}/commons/core/ConfigTest.java (95%) rename src/test/java/{seedu/address => vimification}/commons/core/VersionTest.java (91%) rename src/test/java/{seedu/address => vimification}/commons/core/index/IndexTest.java (95%) rename src/test/java/{seedu/address => vimification}/commons/util/AppUtilTest.java (85%) rename src/test/java/{seedu/address => vimification}/commons/util/CollectionUtilTest.java (90%) rename src/test/java/{seedu/address => vimification}/commons/util/ConfigUtilTest.java (88%) rename src/test/java/{seedu/address => vimification}/commons/util/FileUtilTest.java (84%) rename src/test/java/{seedu/address => vimification}/commons/util/JsonUtilTest.java (63%) rename src/test/java/{seedu/address => vimification}/commons/util/StringUtilTest.java (58%) rename src/test/java/{seedu/address => vimification}/logic/LogicManagerTest.java (66%) rename src/test/java/{seedu/address => vimification}/logic/commands/AddCommandIntegrationTest.java (65%) rename src/test/java/{seedu/address => vimification}/logic/commands/AddCommandTest.java (90%) rename src/test/java/{seedu/address => vimification}/logic/commands/ClearCommandTest.java (59%) rename src/test/java/{seedu/address => vimification}/logic/commands/CommandResultTest.java (86%) rename src/test/java/{seedu/address => vimification}/logic/commands/CommandTestUtil.java (73%) rename src/test/java/{seedu/address => vimification}/logic/commands/DeleteCommandTest.java (71%) rename src/test/java/{seedu/address => vimification}/logic/commands/EditCommandTest.java (66%) rename src/test/java/{seedu/address => vimification}/logic/commands/EditPersonDescriptorTest.java (62%) create mode 100644 src/test/java/vimification/logic/commands/ExitCommandTest.java rename src/test/java/{seedu/address => vimification}/logic/commands/FindCommandTest.java (82%) rename src/test/java/{seedu/address => vimification}/logic/commands/HelpCommandTest.java (61%) rename src/test/java/{seedu/address => vimification}/logic/commands/ListCommandTest.java (65%) create mode 100644 src/test/java/vimification/logic/parser/AddCommandParserTest.java rename src/test/java/{seedu/address => vimification}/logic/parser/AddressBookParserTest.java (64%) rename src/test/java/{seedu/address => vimification}/logic/parser/ArgumentTokenizerTest.java (92%) rename src/test/java/{seedu/address => vimification}/logic/parser/CommandParserTestUtil.java (72%) create mode 100644 src/test/java/vimification/logic/parser/DeleteCommandParserTest.java rename src/test/java/{seedu/address => vimification}/logic/parser/EditCommandParserTest.java (55%) rename src/test/java/{seedu/address => vimification}/logic/parser/FindCommandParserTest.java (57%) rename src/test/java/{seedu/address => vimification}/logic/parser/ParserUtilTest.java (89%) rename src/test/java/{seedu/address => vimification}/model/AddressBookTest.java (73%) rename src/test/java/{seedu/address => vimification}/model/ModelManagerTest.java (84%) rename src/test/java/{seedu/address => vimification}/model/UserPrefsTest.java (86%) rename src/test/java/{seedu/address => vimification}/model/person/AddressTest.java (76%) rename src/test/java/{seedu/address => vimification}/model/person/EmailTest.java (77%) rename src/test/java/{seedu/address => vimification}/model/person/NameContainsKeywordsPredicateTest.java (73%) rename src/test/java/{seedu/address => vimification}/model/person/NameTest.java (93%) rename src/test/java/{seedu/address => vimification}/model/person/PersonTest.java (77%) rename src/test/java/{seedu/address => vimification}/model/person/PhoneTest.java (93%) rename src/test/java/{seedu/address => vimification}/model/person/UniquePersonListTest.java (79%) rename src/test/java/{seedu/address => vimification}/model/tag/TagTest.java (86%) rename src/test/java/{seedu/address => vimification}/storage/JsonAdaptedPersonTest.java (67%) rename src/test/java/{seedu/address => vimification}/storage/JsonAddressBookStorageTest.java (74%) rename src/test/java/{seedu/address => vimification}/storage/JsonSerializableAddressBookTest.java (59%) rename src/test/java/{seedu/address => vimification}/storage/JsonUserPrefsStorageTest.java (84%) rename src/test/java/{seedu/address => vimification}/storage/StorageManagerTest.java (68%) rename src/test/java/{seedu/address => vimification}/testutil/AddressBookBuilder.java (65%) rename src/test/java/{seedu/address => vimification}/testutil/Assert.java (60%) rename src/test/java/{seedu/address => vimification}/testutil/EditPersonDescriptorBuilder.java (84%) rename src/test/java/{seedu/address => vimification}/testutil/PersonBuilder.java (84%) rename src/test/java/{seedu/address => vimification}/testutil/PersonUtil.java (56%) rename src/test/java/{seedu/address => vimification}/testutil/SerializableTestClass.java (98%) rename src/test/java/{seedu/address => vimification}/testutil/TestUtil.java (85%) rename src/test/java/{seedu/address => vimification}/testutil/TypicalIndexes.java (81%) create mode 100644 src/test/java/vimification/testutil/TypicalPersons.java rename src/test/java/{seedu/address => vimification}/ui/TestFxmlObject.java (76%) rename src/test/java/{seedu/address => vimification}/ui/UiPartTest.java (75%) diff --git a/build.gradle b/build.gradle index 7b33adc2f17..c3fa28df4c2 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' +mainClassName = 'vimification.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..e245f3443bc 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -146,7 +146,7 @@ The `Storage` component, ### Common classes -Classes used by multiple components are in the `seedu.addressbook.commons` package. +Classes used by multiple components are in the `vimificationbook.commons` package. -------------------------------------------------------------------------------------------------------------------- diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..b36da311e28 100644 --- a/docs/SettingUp.md +++ b/docs/SettingUp.md @@ -23,7 +23,7 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
:exclamation: Note: Importing a Gradle project is slightly different from importing a normal Java project. 1. **Verify the setup**: - 1. Run the `seedu.address.Main` and try a few commands. + 1. Run the `vimification.Main` and try a few commands. 1. [Run the tests](Testing.md) to ensure they all pass. -------------------------------------------------------------------------------------------------------------------- diff --git a/docs/Testing.md b/docs/Testing.md index 8a99e82438a..ae8c329414f 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -29,8 +29,8 @@ There are two ways to run tests. This project has three types of tests: 1. *Unit tests* targeting the lowest level methods/classes.
- e.g. `seedu.address.commons.StringUtilTest` + e.g. `vimification.commons.StringUtilTest` 1. *Integration tests* that are checking the integration of multiple code units (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest` + e.g. `vimification.storage.StorageManagerTest` 1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest` + e.g. `vimification.logic.LogicManagerTest` diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index 880c701042f..a333378705d 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -23,9 +23,9 @@ For now, let’s keep `RemarkCommand` as simple as possible and print some outpu **`RemarkCommand.java`:** ``` java -package seedu.address.logic.commands; +package vimification.logic.commands; -import seedu.address.model.Model; +import vimification.model.Model; /** * Changes the remark of an existing person in the address book. @@ -91,7 +91,7 @@ Let’s change `RemarkCommand` to parse input from the user. We start by modifying the constructor of `RemarkCommand` to accept an `Index` and a `String`. While we are at it, let’s change the error message to echo the values. While this is not a replacement for tests, it is an obvious way to tell if our code is functioning as intended. ``` java -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static vimification.commons.util.CollectionUtil.requireAllNonNull; //... public class RemarkCommand extends Command { //... @@ -142,7 +142,7 @@ Your code should look something like [this](https://github.com/se-edu/addressboo Now let’s move on to writing a parser that will extract the index and remark from the input provided by the user. -Create a `RemarkCommandParser` class in the `seedu.address.logic.parser` package. The class must extend the `Parser` interface. +Create a `RemarkCommandParser` class in the `vimification.logic.parser` package. The class must extend the `Parser` interface. ![The relationship between Parser and RemarkCommandParser](../images/add-remark/ParserInterface.png) @@ -229,7 +229,7 @@ Now that we have all the information that we need, let’s lay the groundwork fo ### Add a new `Remark` class -Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. +Create a new `Remark` in `vimification.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input validation. @@ -242,7 +242,7 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person. -Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). +Simply add the following to [`vimification.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). **`PersonCard.java`:** diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md index f29169bc924..d317835be10 100644 --- a/docs/tutorials/RemovingFields.md +++ b/docs/tutorials/RemovingFields.md @@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re ### Assisted refactoring -The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. +The `address` field in `Person` is actually an instance of the `vimification.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. * :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences` ![Usages detected](../images/remove/UnsafeDelete.png) diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index 4fb62a83ef6..9c21a8eb419 100644 --- a/docs/tutorials/TracingCode.md +++ b/docs/tutorials/TracingCode.md @@ -39,7 +39,7 @@ In our case, we would want to begin the tracing at the very point where the App -According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`. +According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `vimification.logic.Logic`. @@ -48,7 +48,7 @@ According to the sequence diagram you saw earlier (and repeated above for refere :bulb: **Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`. -A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. +A quick look at the `vimification.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for. ```java public interface Logic { diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java deleted file mode 100644 index 71656d7c5c8..00000000000 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -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 seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Adds a person to the address book. - */ -public class AddCommand extends Command { - - public static final String COMMAND_WORD = "add"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " - + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; - - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; - - private final Person toAdd; - - /** - * Creates an AddCommand to add the specified {@code Person} - */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); - } -} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java deleted file mode 100644 index f866e7133de..00000000000 --- a/src/main/java/seedu/address/model/person/Email.java +++ /dev/null @@ -1,71 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's email in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} - */ -public class Email { - - private static final String SPECIAL_CHARACTERS = "+_.-"; - public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " - + "and adhere to the following constraints:\n" - + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " - + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special " - + "characters.\n" - + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels " - + "separated by periods.\n" - + "The domain name must:\n" - + " - end with a domain label at least 2 characters long\n" - + " - have each domain label start and end with alphanumeric characters\n" - + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any."; - // alphanumeric and special characters - private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters except underscore - private static final String LOCAL_PART_REGEX = "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]" - + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE - + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars - private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX; - public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX; - - public final String value; - - /** - * Constructs an {@code Email}. - * - * @param email A valid email address. - */ - public Email(String email) { - requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); - value = email; - } - - /** - * Returns if a given string is a valid email. - */ - public static boolean isValidEmail(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Email // instanceof handles nulls - && value.equals(((Email) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca7..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java deleted file mode 100644 index 1806da4facf..00000000000 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.model.util; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods for populating {@code AddressBook} with sample data. - */ -public class SampleDataUtil { - 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"), - 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"), - 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"), - 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"), - 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"), - 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"), - getTagSet("colleagues")) - }; - } - - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); - } - return sampleAb; - } - - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - -} diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/vimification/AppParameters.java similarity index 81% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/vimification/AppParameters.java index ab552c398f3..7e0bd69f28b 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/vimification/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package vimification; import java.nio.file.Path; import java.nio.file.Paths; @@ -7,8 +7,8 @@ import java.util.logging.Logger; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.FileUtil; +import vimification.commons.core.LogsCenter; +import vimification.commons.util.FileUtil; /** * Represents the parsed command-line parameters given to the application. @@ -35,10 +35,12 @@ public static AppParameters parse(Application.Parameters parameters) { String configPathParameter = namedParameters.get("config"); if (configPathParameter != null && !FileUtil.isValidPath(configPathParameter)) { - logger.warning("Invalid config path " + configPathParameter + ". Using default config path."); + logger.warning( + "Invalid config path " + configPathParameter + ". Using default config path."); configPathParameter = null; } - appParameters.setConfigPath(configPathParameter != null ? Paths.get(configPathParameter) : null); + appParameters + .setConfigPath(configPathParameter != null ? Paths.get(configPathParameter) : null); return appParameters; } diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/vimification/Main.java similarity index 97% rename from src/main/java/seedu/address/Main.java rename to src/main/java/vimification/Main.java index f159c411084..d75dd744c91 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/vimification/Main.java @@ -1,4 +1,4 @@ -package seedu.address; +package vimification; import javafx.application.Application; diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/vimification/MainApp.java similarity index 65% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/vimification/MainApp.java index 4133aaa0151..986d3a142f1 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/vimification/MainApp.java @@ -1,4 +1,4 @@ -package seedu.address; +package vimification; import java.io.IOException; import java.nio.file.Path; @@ -7,29 +7,29 @@ import javafx.application.Application; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.ConfigUtil; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; -import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.storage.UserPrefsStorage; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; +import vimification.commons.core.Config; +import vimification.commons.core.LogsCenter; +import vimification.commons.core.Version; +import vimification.commons.exceptions.DataConversionException; +import vimification.commons.util.ConfigUtil; +import vimification.commons.util.StringUtil; +import vimification.logic.Logic; +import vimification.logic.LogicManager; +import vimification.model.AddressBook; +import vimification.model.Model; +import vimification.model.ModelManager; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyUserPrefs; +import vimification.model.UserPrefs; +import vimification.model.util.SampleDataUtil; +import vimification.storage.AddressBookStorage; +import vimification.storage.JsonAddressBookStorage; +import vimification.storage.JsonUserPrefsStorage; +import vimification.storage.Storage; +import vimification.storage.StorageManager; +import vimification.storage.UserPrefsStorage; +import vimification.ui.Ui; +import vimification.ui.UiManager; /** * Runs the application. @@ -48,7 +48,8 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info( + "=============================[ Initializing AddressBook ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,7 +57,8 @@ public void init() throws Exception { UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); + AddressBookStorage addressBookStorage = + new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); storage = new StorageManager(addressBookStorage, userPrefsStorage); initLogging(config); @@ -69,9 +71,11 @@ public void init() throws Exception { } /** - * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found, - * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. + * Returns a {@code ModelManager} with the data from {@code storage}'s address book and + * {@code userPrefs}.
+ * The data from the sample address book will be used instead if {@code storage}'s address book + * is not found, or an empty address book will be used instead if errors occur when reading + * {@code storage}'s address book. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { Optional addressBookOptional; @@ -83,10 +87,12 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { } initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); + logger.warning( + "Data file not in the correct format. Will be starting with an empty AddressBook"); initialData = new AddressBook(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning( + "Problem while reading from the file. Will be starting with an empty AddressBook"); initialData = new AddressBook(); } @@ -99,8 +105,8 @@ private void initLogging(Config config) { /** * Returns a {@code Config} using the file at {@code configFilePath}.
- * The default file path {@code Config#DEFAULT_CONFIG_FILE} will be used instead - * if {@code configFilePath} is null. + * The default file path {@code Config#DEFAULT_CONFIG_FILE} will be used instead if + * {@code configFilePath} is null. */ protected Config initConfig(Path configFilePath) { Config initializedConfig; @@ -119,12 +125,13 @@ protected Config initConfig(Path configFilePath) { Optional configOptional = ConfigUtil.readConfig(configFilePathUsed); initializedConfig = configOptional.orElse(new Config()); } catch (DataConversionException e) { - logger.warning("Config file at " + configFilePathUsed + " is not in the correct format. " - + "Using default config properties"); + logger.warning( + "Config file at " + configFilePathUsed + " is not in the correct format. " + + "Using default config properties"); initializedConfig = new Config(); } - //Update config file in case it was missing to begin with or there are new/unused fields + // Update config file in case it was missing to begin with or there are new/unused fields try { ConfigUtil.saveConfig(initializedConfig, configFilePathUsed); } catch (IOException e) { @@ -134,9 +141,8 @@ protected Config initConfig(Path configFilePath) { } /** - * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs file path, - * or a new {@code UserPrefs} with default configuration if errors occur when - * reading from the file. + * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs file path, or a + * new {@code UserPrefs} with default configuration if errors occur when reading from the file. */ protected UserPrefs initPrefs(UserPrefsStorage storage) { Path prefsFilePath = storage.getUserPrefsFilePath(); @@ -151,11 +157,12 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { + "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning( + "Problem while reading from the file. Will be starting with an empty AddressBook"); initializedPrefs = new UserPrefs(); } - //Update prefs file in case it was missing to begin with or there are new/unused fields + // Update prefs file in case it was missing to begin with or there are new/unused fields try { storage.saveUserPrefs(initializedPrefs); } catch (IOException e) { @@ -173,7 +180,8 @@ public void start(Stage primaryStage) { @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info( + "============================ [ Stopping Address Book ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/MainUi.java b/src/main/java/vimification/MainUi.java similarity index 99% rename from src/main/java/seedu/address/MainUi.java rename to src/main/java/vimification/MainUi.java index 256679368ed..45c11a8052f 100644 --- a/src/main/java/seedu/address/MainUi.java +++ b/src/main/java/vimification/MainUi.java @@ -1,4 +1,4 @@ -package seedu.address; +package vimification; import javafx.application.Application; import javafx.application.Platform; diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/vimification/commons/core/Config.java similarity index 93% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/vimification/commons/core/Config.java index 91145745521..875d9700df8 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/vimification/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package vimification.commons.core; import java.nio.file.Path; import java.nio.file.Paths; @@ -37,7 +37,7 @@ public boolean equals(Object other) { if (other == this) { return true; } - if (!(other instanceof Config)) { //this handles null as well. + if (!(other instanceof Config)) { // this handles null as well. return false; } diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/vimification/commons/core/GuiSettings.java similarity index 91% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/vimification/commons/core/GuiSettings.java index ba33653be67..8ad52a0072a 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/vimification/commons/core/GuiSettings.java @@ -1,12 +1,11 @@ -package seedu.address.commons.core; +package vimification.commons.core; import java.awt.Point; import java.io.Serializable; import java.util.Objects; /** - * A Serializable class that contains the GUI settings. - * Guarantees: immutable. + * A Serializable class that contains the GUI settings. Guarantees: immutable. */ public class GuiSettings implements Serializable { @@ -52,7 +51,7 @@ public boolean equals(Object other) { if (other == this) { return true; } - if (!(other instanceof GuiSettings)) { //this handles null as well. + if (!(other instanceof GuiSettings)) { // this handles null as well. return false; } diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/vimification/commons/core/LogsCenter.java similarity index 83% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/vimification/commons/core/LogsCenter.java index 431e7185e76..cd8c4dc11df 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/vimification/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package vimification.commons.core; import java.io.IOException; import java.util.Arrays; @@ -9,11 +9,11 @@ import java.util.logging.SimpleFormatter; /** - * Configures and manages loggers and handlers, including their logging level - * Named {@link Logger}s can be obtained from this class
- * These loggers have been configured to output messages to the console and a {@code .log} file by default, - * at the {@code INFO} level. A new {@code .log} file with a new numbering will be created after the log - * file reaches 5MB big, up to a maximum of 5 files.
+ * Configures and manages loggers and handlers, including their logging level Named {@link Logger}s + * can be obtained from this class
+ * These loggers have been configured to output messages to the console and a {@code .log} file by + * default, at the {@code INFO} level. A new {@code .log} file with a new numbering will be created + * after the log file reaches 5MB big, up to a maximum of 5 files.
*/ public class LogsCenter { private static final int MAX_FILE_COUNT = 5; @@ -25,10 +25,10 @@ public class LogsCenter { private static ConsoleHandler consoleHandler; /** - * Initializes with a custom log level (specified in the {@code config} object) - * Loggers obtained *AFTER* this initialization will have their logging level changed
- * Logging levels for existing loggers will only be updated if the logger with the same name - * is requested again from the LogsCenter. + * Initializes with a custom log level (specified in the {@code config} object) Loggers obtained + * *AFTER* this initialization will have their logging level changed
+ * Logging levels for existing loggers will only be updated if the logger with the same name is + * requested again from the LogsCenter. */ public static void init(Config config) { currentLogLevel = config.getLogLevel(); @@ -95,10 +95,12 @@ private static void addFileHandler(Logger logger) { /** * Creates a {@code FileHandler} for the log file. + * * @throws IOException if there are problems opening the file. */ private static FileHandler createFileHandler() throws IOException { - FileHandler fileHandler = new FileHandler(LOG_FILE, MAX_FILE_SIZE_IN_BYTES, MAX_FILE_COUNT, true); + FileHandler fileHandler = + new FileHandler(LOG_FILE, MAX_FILE_SIZE_IN_BYTES, MAX_FILE_COUNT, true); fileHandler.setFormatter(new SimpleFormatter()); fileHandler.setLevel(currentLogLevel); return fileHandler; diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/vimification/commons/core/Messages.java similarity index 80% rename from src/main/java/seedu/address/commons/core/Messages.java rename to src/main/java/vimification/commons/core/Messages.java index 1deb3a1e469..efb8d60c388 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/vimification/commons/core/Messages.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package vimification.commons.core; /** * Container for user visible messages. @@ -7,7 +7,8 @@ public class Messages { public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; + public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = + "The person index provided is invalid"; public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; } diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/vimification/commons/core/Version.java similarity index 94% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/vimification/commons/core/Version.java index 12142ec1e32..0f753e1bc3e 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/vimification/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package vimification.commons.core; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -50,6 +50,7 @@ public boolean isEarlyAccess() { /** * Parses a version number string in the format V1.2.3. + * * @param versionString version number string * @return a Version object */ @@ -58,7 +59,8 @@ public static Version fromString(String versionString) throws IllegalArgumentExc Matcher versionMatcher = VERSION_PATTERN.matcher(versionString); if (!versionMatcher.find()) { - throw new IllegalArgumentException(String.format(EXCEPTION_STRING_NOT_VERSION, versionString)); + throw new IllegalArgumentException( + String.format(EXCEPTION_STRING_NOT_VERSION, versionString)); } return new Version(Integer.parseInt(versionMatcher.group(1)), diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/vimification/commons/core/index/Index.java similarity index 70% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/vimification/commons/core/index/Index.java index 19536439c09..764aa8ccb0e 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/vimification/commons/core/index/Index.java @@ -1,12 +1,13 @@ -package seedu.address.commons.core.index; +package vimification.commons.core.index; /** * Represents a zero-based or one-based index. * - * {@code Index} should be used right from the start (when parsing in a new user input), so that if the current - * component wants to communicate with another component, it can send an {@code Index} to avoid having to know what - * base the other component is using for its index. However, after receiving the {@code Index}, that component can - * convert it back to an int if the index will not be passed to a different component again. + * {@code Index} should be used right from the start (when parsing in a new user input), so that if + * the current component wants to communicate with another component, it can send an {@code Index} + * to avoid having to know what base the other component is using for its index. However, after + * receiving the {@code Index}, that component can convert it back to an int if the index will not + * be passed to a different component again. */ public class Index { private int zeroBasedIndex; @@ -49,6 +50,6 @@ public static Index fromOneBased(int oneBasedIndex) { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof Index // instanceof handles nulls - && zeroBasedIndex == ((Index) other).zeroBasedIndex); // state check + && zeroBasedIndex == ((Index) other).zeroBasedIndex); // state check } } diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/vimification/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/vimification/commons/exceptions/DataConversionException.java index 1f689bd8e3f..d21e5d3ce8f 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/vimification/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package vimification.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/vimification/commons/exceptions/IllegalValueException.java similarity index 93% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/vimification/commons/exceptions/IllegalValueException.java index 19124db485c..8d996ae1844 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/vimification/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package vimification.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/vimification/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/vimification/commons/util/AppUtil.java index 87aa89c0326..90c50c37375 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/vimification/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package vimification.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import vimification.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/vimification/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/vimification/commons/util/CollectionUtil.java index eafe4dfd681..4b647cb9694 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/vimification/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package vimification.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/vimification/commons/util/ConfigUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/vimification/commons/util/ConfigUtil.java index f7f8a2bd44c..647d0bd2f19 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/vimification/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package vimification.commons.util; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import vimification.commons.core.Config; +import vimification.commons.exceptions.DataConversionException; /** * A class for accessing the Config File. diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/vimification/commons/util/FileUtil.java similarity index 90% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/vimification/commons/util/FileUtil.java index b1e2767cdd9..55c58af1eb6 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/vimification/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package vimification.commons.util; import java.io.IOException; import java.nio.file.Files; @@ -18,8 +18,9 @@ public static boolean isFileExists(Path file) { } /** - * Returns true if {@code path} can be converted into a {@code Path} via {@link Paths#get(String)}, - * otherwise returns false. + * Returns true if {@code path} can be converted into a {@code Path} via + * {@link Paths#get(String)}, otherwise returns false. + * * @param path A string representing the file path. Cannot be null. */ public static boolean isValidPath(String path) { @@ -33,6 +34,7 @@ public static boolean isValidPath(String path) { /** * Creates a file if it does not exist along with its missing parent directories. + * * @throws IOException if the file or directory cannot be created. */ public static void createIfMissing(Path file) throws IOException { @@ -73,8 +75,7 @@ public static String readFromFile(Path file) throws IOException { } /** - * Writes given string to a file. - * Will create the file if it does not exist yet. + * Writes given string to a file. Will create the file if it does not exist yet. */ public static void writeToFile(Path file, String content) throws IOException { Files.write(file, content.getBytes(CHARSET)); diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/vimification/commons/util/JsonUtil.java similarity index 89% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/vimification/commons/util/JsonUtil.java index 8ef609f055d..90c50dab432 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/vimification/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package vimification.commons.util; import static java.util.Objects.requireNonNull; @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +import vimification.commons.core.LogsCenter; +import vimification.commons.exceptions.DataConversionException; /** * Converts a Java object instance to JSON and vice versa @@ -39,7 +39,8 @@ public class JsonUtil { .addSerializer(Level.class, new ToStringSerializer()) .addDeserializer(Level.class, new LevelDeserializer(Level.class))); - static void serializeObjectToJsonFile(Path jsonFile, T objectToSerialize) throws IOException { + static void serializeObjectToJsonFile(Path jsonFile, T objectToSerialize) + throws IOException { FileUtil.writeToFile(jsonFile, toJsonString(objectToSerialize)); } @@ -49,10 +50,13 @@ static T deserializeObjectFromJsonFile(Path jsonFile, Class classOfObject } /** - * Returns the Json object from the given file or {@code Optional.empty()} object if the file is not found. - * If any values are missing from the file, default values will be used, as long as the file is a valid json file. + * Returns the Json object from the given file or {@code Optional.empty()} object if the file is + * not found. If any values are missing from the file, default values will be used, as long as + * the file is a valid json file. + * * @param filePath cannot be null. - * @param classOfObjectToDeserialize Json file has to correspond to the structure in the class given here. + * @param classOfObjectToDeserialize Json file has to correspond to the structure in the class + * given here. * @throws DataConversionException if the file format is not as expected. */ public static Optional readJsonFile( @@ -77,8 +81,9 @@ public static Optional readJsonFile( } /** - * Saves the Json object to the specified file. - * Overwrites existing file if it exists, creates a new file if it doesn't. + * Saves the Json object to the specified file. Overwrites existing file if it exists, creates a + * new file if it doesn't. + * * @param jsonFile cannot be null * @param filePath cannot be null * @throws IOException if there was an error during writing to the file @@ -93,6 +98,7 @@ public static void saveJsonFile(T jsonFile, Path filePath) throws IOExceptio /** * Converts a given string representation of a JSON data to instance of a class + * * @param The generic type to create an instance of * @return The instance of T with the specified values in the JSON string */ @@ -102,6 +108,7 @@ public static T fromJsonString(String json, Class instanceClass) throws I /** * Converts a given instance of a class into its JSON data string representation + * * @param instance The T object to be converted into the JSON string * @param The generic type to create an instance of * @return JSON data representation of the given class instance, in string diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/vimification/commons/util/StringUtil.java similarity index 72% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/vimification/commons/util/StringUtil.java index 61cc8c9a1cb..99c02b5fe2b 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/vimification/commons/util/StringUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package vimification.commons.util; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static vimification.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; @@ -13,13 +13,16 @@ public class StringUtil { /** - * Returns true if the {@code sentence} contains the {@code word}. - * Ignores case, but a full word match is required. - *
examples:
+     * Returns true if the {@code sentence} contains the {@code word}. Ignores case, but a full word
+     * match is required. 
+ * examples: + * + *
      *       containsWordIgnoreCase("ABc def", "abc") == true
      *       containsWordIgnoreCase("ABc def", "DEF") == true
      *       containsWordIgnoreCase("ABc def", "AB") == false //not a full word match
-     *       
+ *
+ * * @param sentence cannot be null * @param word cannot be null, cannot be empty, must be a single word */ @@ -29,7 +32,8 @@ public static boolean containsWordIgnoreCase(String sentence, String word) { String preppedWord = word.trim(); checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty"); - checkArgument(preppedWord.split("\\s+").length == 1, "Word parameter should be a single word"); + checkArgument(preppedWord.split("\\s+").length == 1, + "Word parameter should be a single word"); String preppedSentence = sentence; String[] wordsInPreppedSentence = preppedSentence.split("\\s+"); @@ -49,10 +53,11 @@ public static String getDetails(Throwable t) { } /** - * Returns true if {@code s} represents a non-zero unsigned integer - * e.g. 1, 2, 3, ..., {@code Integer.MAX_VALUE}
- * Will return false for any other non-null string input - * e.g. empty string, "-1", "0", "+1", and " 2 " (untrimmed), "3 0" (contains whitespace), "1 a" (contains letters) + * Returns true if {@code s} represents a non-zero unsigned integer e.g. 1, 2, 3, ..., + * {@code Integer.MAX_VALUE}
+ * Will return false for any other non-null string input e.g. empty string, "-1", "0", "+1", and + * " 2 " (untrimmed), "3 0" (contains whitespace), "1 a" (contains letters) + * * @throws NullPointerException if {@code s} is null. */ public static boolean isNonZeroUnsignedInteger(String s) { @@ -60,7 +65,8 @@ public static boolean isNonZeroUnsignedInteger(String s) { try { int value = Integer.parseInt(s); - return value > 0 && !s.startsWith("+"); // "+1" is successfully parsed by Integer#parseInt(String) + return value > 0 && !s.startsWith("+"); // "+1" is successfully parsed by + // Integer#parseInt(String) } catch (NumberFormatException nfe) { return false; } diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/vimification/logic/Logic.java similarity index 73% rename from src/main/java/seedu/address/logic/Logic.java rename to src/main/java/vimification/logic/Logic.java index 92cd8fa605a..8b052e6df3f 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/vimification/logic/Logic.java @@ -1,14 +1,14 @@ -package seedu.address.logic; +package vimification.logic; import java.nio.file.Path; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import vimification.commons.core.GuiSettings; +import vimification.logic.commands.CommandResult; +import vimification.logic.commands.exceptions.CommandException; +import vimification.logic.parser.exceptions.ParseException; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.person.Person; /** * API of the Logic component @@ -16,6 +16,7 @@ public interface Logic { /** * Executes the command and returns the result. + * * @param commandText The command as entered by the user. * @return the result of the command execution. * @throws CommandException If an error occurs during command execution. @@ -26,7 +27,7 @@ public interface Logic { /** * Returns the AddressBook. * - * @see seedu.address.model.Model#getAddressBook() + * @see vimification.model.Model#getAddressBook() */ ReadOnlyAddressBook getAddressBook(); diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/vimification/logic/LogicManager.java similarity index 77% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/vimification/logic/LogicManager.java index 9d9c6d15bdc..2bfb31ed1db 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/vimification/logic/LogicManager.java @@ -1,21 +1,21 @@ -package seedu.address.logic; +package vimification.logic; import java.io.IOException; import java.nio.file.Path; import java.util.logging.Logger; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; -import seedu.address.storage.Storage; +import vimification.commons.core.GuiSettings; +import vimification.commons.core.LogsCenter; +import vimification.logic.commands.Command; +import vimification.logic.commands.CommandResult; +import vimification.logic.commands.exceptions.CommandException; +import vimification.logic.parser.AddressBookParser; +import vimification.logic.parser.exceptions.ParseException; +import vimification.model.Model; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.person.Person; +import vimification.storage.Storage; /** * The main LogicManager of the app. diff --git a/src/main/java/vimification/logic/commands/AddCommand.java b/src/main/java/vimification/logic/commands/AddCommand.java new file mode 100644 index 00000000000..bfa876a760b --- /dev/null +++ b/src/main/java/vimification/logic/commands/AddCommand.java @@ -0,0 +1,69 @@ +package vimification.logic.commands; + +import static java.util.Objects.requireNonNull; +import static vimification.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static vimification.logic.parser.CliSyntax.PREFIX_EMAIL; +import static vimification.logic.parser.CliSyntax.PREFIX_NAME; +import static vimification.logic.parser.CliSyntax.PREFIX_PHONE; +import static vimification.logic.parser.CliSyntax.PREFIX_TAG; + +import vimification.logic.commands.exceptions.CommandException; +import vimification.model.Model; +import vimification.model.person.Person; + +/** + * Adds a person to the address book. + */ +public class AddCommand extends Command { + + public static final String COMMAND_WORD = "add"; + + public static final String MESSAGE_USAGE = + COMMAND_WORD + ": Adds a person to the address book. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + PREFIX_ADDRESS + "ADDRESS " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "johnd@example.com " + + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " + + PREFIX_TAG + "friends " + + PREFIX_TAG + "owesMoney"; + + public static final String MESSAGE_SUCCESS = "New person added: %1$s"; + public static final String MESSAGE_DUPLICATE_PERSON = + "This person already exists in the address book"; + + private final Person toAdd; + + /** + * Creates an AddCommand to add the specified {@code Person} + */ + public AddCommand(Person person) { + requireNonNull(person); + toAdd = person; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasPerson(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_PERSON); + } + + model.addPerson(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddCommand // instanceof handles nulls + && toAdd.equals(((AddCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/vimification/logic/commands/ClearCommand.java similarity index 80% rename from src/main/java/seedu/address/logic/commands/ClearCommand.java rename to src/main/java/vimification/logic/commands/ClearCommand.java index 9c86b1fa6e4..c54da978908 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/vimification/logic/commands/ClearCommand.java @@ -1,9 +1,9 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static java.util.Objects.requireNonNull; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; +import vimification.model.AddressBook; +import vimification.model.Model; /** * Clears the address book. diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/vimification/logic/commands/Command.java similarity index 78% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/vimification/logic/commands/Command.java index 64f18992160..28b5bf009f0 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/vimification/logic/commands/Command.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import vimification.logic.commands.exceptions.CommandException; +import vimification.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/vimification/logic/commands/CommandResult.java similarity index 93% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/vimification/logic/commands/CommandResult.java index 92f900b7916..4dd66bce0f5 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/vimification/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static java.util.Objects.requireNonNull; @@ -27,8 +27,8 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { } /** - * Constructs a {@code CommandResult} with the specified {@code feedbackToUser}, - * and other fields set to their default value. + * Constructs a {@code CommandResult} with the specified {@code feedbackToUser}, and other + * fields set to their default value. */ public CommandResult(String feedbackToUser) { this(feedbackToUser, false, false); diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/vimification/logic/commands/DeleteCommand.java similarity index 80% rename from src/main/java/seedu/address/logic/commands/DeleteCommand.java rename to src/main/java/vimification/logic/commands/DeleteCommand.java index 02fd256acba..ce7587f0953 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/vimification/logic/commands/DeleteCommand.java @@ -1,14 +1,14 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static java.util.Objects.requireNonNull; import java.util.List; -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; +import vimification.commons.core.Messages; +import vimification.commons.core.index.Index; +import vimification.logic.commands.exceptions.CommandException; +import vimification.model.Model; +import vimification.model.person.Person; /** * Deletes a person identified using it's displayed index from the address book. @@ -48,6 +48,6 @@ public CommandResult execute(Model model) throws CommandException { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof DeleteCommand // instanceof handles nulls - && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check + && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check } } diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/vimification/logic/commands/EditCommand.java similarity index 70% rename from src/main/java/seedu/address/logic/commands/EditCommand.java rename to src/main/java/vimification/logic/commands/EditCommand.java index 7e36114902f..a5d922683dd 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/vimification/logic/commands/EditCommand.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -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.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static vimification.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static vimification.logic.parser.CliSyntax.PREFIX_EMAIL; +import static vimification.logic.parser.CliSyntax.PREFIX_NAME; +import static vimification.logic.parser.CliSyntax.PREFIX_PHONE; +import static vimification.logic.parser.CliSyntax.PREFIX_TAG; +import static vimification.model.Model.PREDICATE_SHOW_ALL_PERSONS; import java.util.Collections; import java.util.HashSet; @@ -14,17 +14,17 @@ import java.util.Optional; import java.util.Set; -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.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import vimification.commons.core.Messages; +import vimification.commons.core.index.Index; +import vimification.commons.util.CollectionUtil; +import vimification.logic.commands.exceptions.CommandException; +import vimification.model.Model; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Person; +import vimification.model.person.Phone; +import vimification.model.tag.Tag; /** * Edits the details of an existing person in the address book. @@ -33,22 +33,24 @@ public class EditCommand extends Command { public static final String COMMAND_WORD = "edit"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " - + "Existing values will be overwritten by the input values.\n" - + "Parameters: INDEX (must be a positive integer) " - + "[" + PREFIX_NAME + "NAME] " - + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; + public static final String MESSAGE_USAGE = + COMMAND_WORD + ": Edits the details of the person identified " + + "by the index number used in the displayed person list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_EMAIL + "EMAIL] " + + "[" + PREFIX_ADDRESS + "ADDRESS] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_PHONE + "91234567 " + + PREFIX_EMAIL + "johndoe@example.com"; public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; + public static final String MESSAGE_DUPLICATE_PERSON = + "This person already exists in the address book."; private final Index index; private final EditPersonDescriptor editPersonDescriptor; @@ -87,16 +89,18 @@ public CommandResult execute(Model model) throws CommandException { } /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. + * Creates and returns a {@code Person} with the details of {@code personToEdit} edited with + * {@code editPersonDescriptor}. */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { + private static Person createEditedPerson(Person personToEdit, + EditPersonDescriptor editPersonDescriptor) { assert personToEdit != null; Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); + Address updatedAddress = + editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); @@ -134,8 +138,7 @@ public static class EditPersonDescriptor { public EditPersonDescriptor() {} /** - * Copy constructor. - * A defensive copy of {@code tags} is used internally. + * Copy constructor. A defensive copy of {@code tags} is used internally. */ public EditPersonDescriptor(EditPersonDescriptor toCopy) { setName(toCopy.name); @@ -185,20 +188,20 @@ public Optional
getAddress() { } /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. + * Sets {@code tags} to this object's {@code tags}. A defensive copy of {@code tags} is used + * internally. */ public void setTags(Set tags) { this.tags = (tags != null) ? new HashSet<>(tags) : null; } /** - * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. + * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} if + * modification is attempted. Returns {@code Optional#empty()} if {@code tags} is null. */ public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); + return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) + : Optional.empty(); } @Override diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/vimification/logic/commands/ExitCommand.java similarity index 60% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/vimification/logic/commands/ExitCommand.java index 3dd85a8ba90..8699f757826 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/vimification/logic/commands/ExitCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; -import seedu.address.model.Model; +import vimification.model.Model; /** * Terminates the program. @@ -9,7 +9,8 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = + "Exiting Address Book as requested ..."; @Override public CommandResult execute(Model model) { diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/vimification/logic/commands/FindCommand.java similarity index 71% rename from src/main/java/seedu/address/logic/commands/FindCommand.java rename to src/main/java/vimification/logic/commands/FindCommand.java index d6b19b0a0de..1a0356a4318 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/vimification/logic/commands/FindCommand.java @@ -1,10 +1,10 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static java.util.Objects.requireNonNull; -import seedu.address.commons.core.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import vimification.commons.core.Messages; +import vimification.model.Model; +import vimification.model.person.NameContainsKeywordsPredicate; /** * Finds and lists all persons in address book whose name contains any of the argument keywords. @@ -14,7 +14,8 @@ public class FindCommand extends Command { public static final String COMMAND_WORD = "find"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Finds all persons whose names contain any of " + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + "Example: " + COMMAND_WORD + " alice bob charlie"; @@ -30,13 +31,14 @@ public CommandResult execute(Model model) { requireNonNull(model); model.updateFilteredPersonList(predicate); return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, + model.getFilteredPersonList().size())); } @Override public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof FindCommand // instanceof handles nulls - && predicate.equals(((FindCommand) other).predicate)); // state check + && predicate.equals(((FindCommand) other).predicate)); // state check } } diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/vimification/logic/commands/HelpCommand.java similarity index 62% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/vimification/logic/commands/HelpCommand.java index bf824f91bd0..2a7aa82672d 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/vimification/logic/commands/HelpCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; -import seedu.address.model.Model; +import vimification.model.Model; /** * Format full help instructions for every command for display. @@ -9,8 +9,9 @@ public class HelpCommand extends Command { public static final String COMMAND_WORD = "help"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Shows program usage instructions.\n" - + "Example: " + COMMAND_WORD; + public static final String MESSAGE_USAGE = + COMMAND_WORD + ": Shows program usage instructions.\n" + + "Example: " + COMMAND_WORD; public static final String SHOWING_HELP_MESSAGE = "Opened help window."; diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/vimification/logic/commands/ListCommand.java similarity index 78% rename from src/main/java/seedu/address/logic/commands/ListCommand.java rename to src/main/java/vimification/logic/commands/ListCommand.java index 84be6ad2596..939693bddd7 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/vimification/logic/commands/ListCommand.java @@ -1,9 +1,9 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static vimification.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import seedu.address.model.Model; +import vimification.model.Model; /** * Lists all persons in the address book to the user. diff --git a/src/main/java/seedu/address/logic/commands/RemarkCommand.java b/src/main/java/vimification/logic/commands/RemarkCommand.java similarity index 75% rename from src/main/java/seedu/address/logic/commands/RemarkCommand.java rename to src/main/java/vimification/logic/commands/RemarkCommand.java index ad3d2cf931e..c58fe0b9518 100644 --- a/src/main/java/seedu/address/logic/commands/RemarkCommand.java +++ b/src/main/java/vimification/logic/commands/RemarkCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; -import seedu.address.model.Model; +import vimification.model.Model; public class RemarkCommand extends Command { diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/vimification/logic/commands/exceptions/CommandException.java similarity index 79% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/vimification/logic/commands/exceptions/CommandException.java index a16bd14f2cd..0956d2811e0 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/vimification/logic/commands/exceptions/CommandException.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands.exceptions; +package vimification.logic.commands.exceptions; /** * Represents an error which occurs during execution of a {@link Command}. @@ -9,7 +9,8 @@ public CommandException(String message) { } /** - * Constructs a new {@code CommandException} with the specified detail {@code message} and {@code cause}. + * Constructs a new {@code CommandException} with the specified detail {@code message} and + * {@code cause}. */ public CommandException(String message, Throwable cause) { super(message, cause); diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/vimification/logic/parser/AddCommandParser.java similarity index 52% rename from src/main/java/seedu/address/logic/parser/AddCommandParser.java rename to src/main/java/vimification/logic/parser/AddCommandParser.java index 3b8bfa035e8..54aaa271403 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/vimification/logic/parser/AddCommandParser.java @@ -1,23 +1,23 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -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 vimification.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static vimification.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static vimification.logic.parser.CliSyntax.PREFIX_EMAIL; +import static vimification.logic.parser.CliSyntax.PREFIX_NAME; +import static vimification.logic.parser.CliSyntax.PREFIX_PHONE; +import static vimification.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Set; import java.util.stream.Stream; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import vimification.logic.commands.AddCommand; +import vimification.logic.parser.exceptions.ParseException; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Person; +import vimification.model.person.Phone; +import vimification.model.tag.Tag; /** * Parses input arguments and creates a new AddCommand object @@ -25,17 +25,21 @@ public class AddCommandParser implements Parser { /** - * Parses the given {@code String} of arguments in the context of the AddCommand - * and returns an AddCommand object for execution. + * Parses the given {@code String} of arguments in the context of the AddCommand and returns an + * AddCommand object for execution. + * * @throws ParseException if the user input does not conform the expected format */ public AddCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, + PREFIX_ADDRESS, PREFIX_TAG); - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, + PREFIX_EMAIL) || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); @@ -53,8 +57,10 @@ public AddCommand parse(String args) throws ParseException { * Returns true if none of the prefixes contains empty {@code Optional} values in the given * {@code ArgumentMultimap}. */ - private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { - return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, + Prefix... prefixes) { + return Stream.of(prefixes) + .allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); } } diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/vimification/logic/parser/AddressBookParser.java similarity index 63% rename from src/main/java/seedu/address/logic/parser/AddressBookParser.java rename to src/main/java/vimification/logic/parser/AddressBookParser.java index 1e466792b46..2ecb4dfb017 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/vimification/logic/parser/AddressBookParser.java @@ -1,21 +1,21 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static vimification.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static vimification.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; import java.util.regex.Matcher; import java.util.regex.Pattern; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.Command; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; +import vimification.logic.commands.AddCommand; +import vimification.logic.commands.ClearCommand; +import vimification.logic.commands.Command; +import vimification.logic.commands.DeleteCommand; +import vimification.logic.commands.EditCommand; +import vimification.logic.commands.ExitCommand; +import vimification.logic.commands.FindCommand; +import vimification.logic.commands.HelpCommand; +import vimification.logic.commands.ListCommand; +import vimification.logic.parser.exceptions.ParseException; /** * Parses user input. @@ -25,7 +25,8 @@ public class AddressBookParser { /** * Used for initial separation of command word and args. */ - private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + private static final Pattern BASIC_COMMAND_FORMAT = + Pattern.compile("(?\\S+)(?.*)"); /** * Parses user input into command for execution. @@ -37,7 +38,8 @@ public class AddressBookParser { public Command parseCommand(String userInput) throws ParseException { final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); if (!matcher.matches()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); } final String commandWord = matcher.group("commandWord"); diff --git a/src/main/java/vimification/logic/parser/ApplicativeParser.java b/src/main/java/vimification/logic/parser/ApplicativeParser.java new file mode 100644 index 00000000000..b102cad435a --- /dev/null +++ b/src/main/java/vimification/logic/parser/ApplicativeParser.java @@ -0,0 +1,521 @@ +package vimification.logic.parser; + +import java.util.Arrays; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Basic implementation of a generic {@code ApplicativeParser}, based on functional applicative + * parser from the Haskell programming language. + *

+ * Uses static methods to create instances of this class. + * + * @param the type of the parser result + */ +public class ApplicativeParser { + + /////////////////////////////////// + // PREDEFINED PARSER COMBINATORS // + /////////////////////////////////// + + private static final ApplicativeParser ALWAYS_FAILED_PARSER = + fromRunner(ignore -> Optional.empty()); + + private static final ApplicativeParser EOF_PARSER = fromRunner( + input -> input.isEmpty() ? Optional.of(Pair.of(input, null)) : Optional.empty()); + + private static final ApplicativeParser SKIP_WHITESPACES_PARSER = fromRunner(input -> { + int length = input.length(); + int offset = 0; + while (offset < length && Character.isWhitespace(input.charAt(offset))) { + offset++; + } + return Optional.of(Pair.of(input.subview(offset), null)); + }); + + private static final ApplicativeParser NON_WHITESPACES_PARSER = fromRunner(input -> { + int length = input.length(); + int offset = 0; + while (offset < length && !Character.isWhitespace(input.charAt(offset))) { + offset++; + } + String value = input.substringTo(offset); + return value.isEmpty() + ? Optional.empty() + : Optional.of(Pair.of(input.subview(offset), input.substringTo(offset))); + }); + + private static final ApplicativeParser UNTIL_EOF_PARSER = fromRunner(input -> { + int length = input.length(); + return Optional.of(Pair.of(input.subview(length), input.substringTo(length))); + }); + + ///////////////////////////////////// + // INSTANCE FIELDS AND CONSTRUCTOR // + ///////////////////////////////////// + + private final Function>> runner; + private final String errorMessage; + + private ApplicativeParser( + Function>> runner, + String errorMessage) { + this.runner = runner; + this.errorMessage = errorMessage; + } + + ////////////////////// + // STATIC UTILITIES // + ////////////////////// + + private static ApplicativeParser fromRunner( + Function>> runner) { + return new ApplicativeParser<>(runner, null); + } + + /** + * Creates a parser that parses nothing and returns exactly the given value. This method is + * equivalent to {@code return} function in Haskell. + * + * @param the type of the given value + * @param value the given value + * @return a parser that parses nothing, and returns exactly the given value + */ + public static ApplicativeParser of(T value) { + return fromRunner(input -> Optional.of(Pair.of(input, value))); + } + + /** + * Returns a parser that always fails. + * + * @param the type of the parser result (which does not exist, in this case) + * @return a parser that always fails + */ + public static ApplicativeParser fail() { + return ALWAYS_FAILED_PARSER.cast(); + } + + /** + * Lifts a normal binary function to work on parsers. The first parser is run on the input, and + * may consume some characters. Then, the second parser is run on the remaining input. Finally, + * the results of the two parsers are combined using the binary function. + *

+ * This method is equivalent to {@code liftA2} function in Haskell. + *

+ * This method proves that an {@code ApplicativeParser} is an {@code Applicative}. However, + * there is no way to encode the behavior of an {@code Applicative} using an instance method in + * Java. + * + * @param the type of the first parser result + * @param the type of the second parser result + * @param the type of the resultant parser result + * @param combiner the given binary function + * @param left the first parser + * @param right the second parser + * @return a new parser, that first runs the first parser, then the second parser, and finally + * combines the result of these parsers using the binary function + */ + public static ApplicativeParser lift( + BiFunction combiner, + ApplicativeParser left, + ApplicativeParser right) { + return left.flatMap(leftValue -> right + .flatMap(rightValue -> ApplicativeParser + .of(combiner.apply(leftValue, rightValue)))); + } + + /** + * Returns a parser that succeeds when reaches eof, and fails otherwise. The result of this + * parser should be discarded. + * + * @return the eof parser + */ + public static ApplicativeParser eof() { + return EOF_PARSER; + } + + /** + * Returns a parser that skips all leading whitespaces. + * + * @return a parser that skips all leading whitespaces + */ + public static ApplicativeParser skipWhitespaces() { + return SKIP_WHITESPACES_PARSER; + } + + /** + * Returns a parser that consumes input until it reaches a whitespace character. + * + * @return a parser that parses until a whitespace character + */ + public static ApplicativeParser parseNonWhitespaces() { + return NON_WHITESPACES_PARSER; + } + + /** + * Returns a parser that consumes all remaining input. + * + * @return a parser that parses until eof + */ + public static ApplicativeParser parseUntilEof() { + return UNTIL_EOF_PARSER; + } + + /** + * Returns a parser that succeeds when the input starts with the given prefix, fails otherwise. + * If the parser succeeds, the result of the parser will be the given prefix. + * + * @param prefix the given prefix + * @return a parser that succeeds when the input starts with the given prefix, fails otherwise + */ + public static ApplicativeParser parseString(String prefix) { + return fromRunner(input -> input.startsWith(prefix) + ? Optional.of(Pair.of(input.subview(prefix.length()), prefix)) + : Optional.empty()); + } + + /** + * Returns a parser that parses until the given substring is encountered. The substring will + * also be consumed from the input. This parser will returns the substring between the start of + * the input, and the last character before the given substring. + * + * @param end the substring to find + * @return a parser that parses until the given substring is encountered + */ + public static ApplicativeParser parseUntil(String end) { + return fromRunner(input -> { + int offset = input.indexOf(end); + return offset < 0 + ? Optional.empty() + : Optional.of(Pair.of( + input.subview(offset + end.length()), + input.substringTo(offset))); + }); + } + + /** + * Returns a parser that tries multiple parsers, until one succeeds. + * + * @param the type of the parser results + * @param parsers an array of parsers + * @return a parser that tries the given parsers, until one succeeds + */ + @SafeVarargs + public static ApplicativeParser choice(ApplicativeParser... parsers) { + return Arrays.stream(parsers) + .reduce(ApplicativeParser::or) + .orElseGet(ApplicativeParser::fail); + } + + ////////////////////// + // INSTANCE METHODS // + ////////////////////// + + private Optional> run(StringView input) { + Optional> result = runner.apply(input); + if (errorMessage != null && result.isEmpty()) { + throw new ParserException(errorMessage); + } + return result; + } + + @SuppressWarnings("unchecked") + private ApplicativeParser cast() { + return (ApplicativeParser) this; + } + + /** + * Returns a parser that runs this parser and another parser, but drops the result of that + * parser. This method is equivalent to {@code <*} operator in Haskell. + * + * @param the type of the other parser result + * @param that the other parser + * @return a new parser that runs both parsers, but keeps the result of only one parser + */ + public ApplicativeParser dropNext(ApplicativeParser that) { + return lift((left, right) -> left, this, that); + } + + /** + * Returns a parser that runs this parser and another parser, but takes the result of that + * parser. This method is equivalent to {@code *>} operator in Haskell. + * + * @param the type of the other parser result + * @param that the other parser + * @return a new parser that runs both parsers, but keeps the result of only one parser + */ + public ApplicativeParser takeNext(ApplicativeParser that) { + return lift((left, right) -> right, this, that); + } + + /** + * Maps a function over the result of this parser. + *

+ * This method is equivalent to {@code fmap} function in Haskell. + *

+ * This method proves that an {@code ApplicativeParser} is a {@code Functor}. + * + * @param the type of the new result + * @param mapper the function to map the result of this parser + * @return a new parser that applies the mapper function, after running this parser + */ + public ApplicativeParser map(Function mapper) { + return fromRunner(runner.andThen(opt -> opt.map(pair -> { + StringView input = pair.getFirst(); + T oldValue = pair.getSecond(); + return Pair.of(input, mapper.apply(oldValue)); + }))); + } + + /** + * Runs this parser, then uses the given function to create another parser from the result of + * this parser and run that parser. + *

+ * This method is equivalent to {@code >>=} operator in Haskell. + *

+ * This method proves that an {@code ApplicativeParser} is a {@code Monad}. + * + * @param the type of the generated parser result + * @param flatMapper the function to create another parser from the result of this parser + * @return a parser that runs this parser, and then uses the mapper function to create and run + * another parser + */ + public ApplicativeParser flatMap( + Function> flatMapper) { + return fromRunner(input -> run(input).flatMap(pair -> { + StringView oldInput = pair.getFirst(); + T oldValue = pair.getSecond(); + ApplicativeParser that = flatMapper.apply(oldValue); + return that.cast().run(oldInput); + })); + } + + /** + * Maps a function that produces an {@code Optional} over the result of this parser, and unwraps + * that {@code Optional}. If the {@code Optional} is empty, the parser fails. + * + * @param the type of the new result + * @param optionalMapper the function to map the result of this parser + * @return a new parser that applies the mapper function, after running this parser + */ + public ApplicativeParser optionalMap( + Function> optionalMapper) { + return flatMap(optionalMapper.andThen(opt -> opt + .map(ApplicativeParser::of) + .orElseGet(ApplicativeParser::fail))).cast(); + } + + /** + * Uses a predicate to filter the result of this parser. If the test fails, the parser also + * fails. + * + * @param predicate the predicate to test the result of this parser + * @return a new parser that runs this parser and uses the predicate to test the result + */ + public ApplicativeParser filter(Predicate predicate) { + return fromRunner(input -> run(input).filter(pair -> predicate.test(pair.getSecond()))); + } + + /** + * Chooses between this parser, or another parser. + * + * @param that the other parser + * @return a parser that chooses the result of the first succeeds parser + */ + public ApplicativeParser or(ApplicativeParser that) { + return fromRunner(input -> run(input).or(() -> that.run(input))); + } + + /** + * Returns a new parser that throws an exception (immediately) if this parser fails. The thrown + * exception will stop a parsing pipeline. + * + * @param errorMessage the error message of the exception + * @return a new parser that throws if this parser fails + */ + public ApplicativeParser throwIfFail(String errorMessage) { + return new ApplicativeParser<>(runner, errorMessage); + } + + /** + * Returns a parser that does nothing when fails. + * + * @return a parser that does nothing when fails. + */ + public ApplicativeParser ignoreIfFail() { + return fromRunner(runner); + } + + /** + * Runs this parser on the given input. + * + * @param input the given input, to be parsed + * @return a pair consists of the remaining input, and the parser result + * @throws ParserException if this parser fails + */ + public Pair parse(String input) { + return run(StringView.of(input, 0)) + .map(pair -> Pair.of(pair.getFirst().toString(), pair.getSecond())) + .orElseThrow(() -> new ParserException("Unable to parse input: " + input)); + } +} + + +/** + * Wrapper of a normal {@code String} instance. Instances of this class are used to represent slices + * of the original string. + *

+ * A {@code StringView} instance stores 2 fields: the original string and an index that represents + * the offset into the original string. + */ +class StringView { + + private String value; + private int index; + + private StringView(String value, int index) { + this.value = value; + this.index = index; + } + + /** + * Creates a new instance of this class. + * + * @param value the original string + * @param index the initial offset + * @return an instance of this class, which represents a substring that starts from + * {@code index} in the original string. + */ + static StringView of(String value, int index) { + return new StringView(value, index); + } + + /** + * Checks whether this view starts with the given prefix. + * + * @param prefix the prefix to check + * @return whether this view starts with the given prefix + */ + boolean startsWith(String prefix) { + return value.startsWith(prefix, index); + } + + /** + * Returns a subview of this view, starting from an offset. + * + * @param offset the offset from the start of this view + * @return a new subview that starts from the given offset + */ + StringView subview(int offset) { + return new StringView(value, index + offset); + } + + /** + * Returns a substring, from the start of this view to the given offset. + * + * @param offset the offset from the start of this view + * @return a substring that starts at the start of this view, and ends before the given offset + */ + String substringTo(int offset) { + return value.substring(index, index + offset); + } + + /** + * Returns the character at the given offset. + * + * @param offset the offset from the start of this view + * @return the chatacter at the offset + */ + char charAt(int offset) { + return value.charAt(index + offset); + } + + /** + * Returns the length of this view. + * + * @return the length of this view + */ + int length() { + return value.length() - index; + } + + /** + * Returns the index of the first occurence of the given substring in this view. + * + * @param str the given substring to find + * @return the index of the first occurence of the given substring in this view, {@code -1} if + * the substring does not appear in this view + */ + int indexOf(String str) { + int result = value.indexOf(str, index); + return result < 0 ? result : result - index; + } + + /** + * Checks whether this view represents an empty string. + * + * @return {@code true} if this view represents an empty string, otherwise {@code false} + */ + boolean isEmpty() { + return index >= value.length(); + } + + @Override + public String toString() { + return value.substring(index); + } +} + + +/** + * Represents a tuple of two elements. + * + * @param the type of the first element + * @param the type of the second element + */ +class Pair { + + private T first; + private U second; + + /** + * Creates a new pair of two elements. + * + * @param first the first element + * @param second the second element + */ + public Pair(T first, U second) { + this.first = first; + this.second = second; + } + + /** + * Creates a new pair of two elements. This is a factory method, used to reduce the verbosity + * when creating a new pair. + * + * @param the type of the first element + * @param the type of the second element + * @param first the first element + * @param second the second element + * @return a new pair that contains the two elements + */ + public static Pair of(T first, U second) { + return new Pair<>(first, second); + } + + public T getFirst() { + return first; + } + + public U getSecond() { + return second; + } + + @Override + public String toString() { + return String.format("[%s, %s]", first, second); + } +} + diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/vimification/logic/parser/ArgumentMultimap.java similarity index 58% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/vimification/logic/parser/ArgumentMultimap.java index 954c8e18f8e..391ab7ace7f 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/vimification/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; import java.util.ArrayList; import java.util.HashMap; @@ -7,22 +7,22 @@ import java.util.Optional; /** - * Stores mapping of prefixes to their respective arguments. - * Each key may be associated with multiple argument values. - * Values for a given key are stored in a list, and the insertion ordering is maintained. - * Keys are unique, but the list of argument values may contain duplicate argument values, i.e. the same argument value - * can be inserted multiple times for the same prefix. + * Stores mapping of prefixes to their respective arguments. Each key may be associated with + * multiple argument values. Values for a given key are stored in a list, and the insertion ordering + * is maintained. Keys are unique, but the list of argument values may contain duplicate argument + * values, i.e. the same argument value can be inserted multiple times for the same prefix. */ public class ArgumentMultimap { - /** Prefixes mapped to their respective arguments**/ + /** Prefixes mapped to their respective arguments **/ private final Map> argMultimap = new HashMap<>(); /** - * Associates the specified argument value with {@code prefix} key in this map. - * If the map previously contained a mapping for the key, the new value is appended to the list of existing values. + * Associates the specified argument value with {@code prefix} key in this map. If the map + * previously contained a mapping for the key, the new value is appended to the list of existing + * values. * - * @param prefix Prefix key with which the specified argument value is to be associated + * @param prefix Prefix key with which the specified argument value is to be associated * @param argValue Argument value to be associated with the specified prefix key */ public void put(Prefix prefix, String argValue) { @@ -40,9 +40,9 @@ public Optional getValue(Prefix prefix) { } /** - * Returns all values of {@code prefix}. - * If the prefix does not exist or has no values, this will return an empty list. - * Modifying the returned list will not affect the underlying data structure of the ArgumentMultimap. + * Returns all values of {@code prefix}. If the prefix does not exist or has no values, this + * will return an empty list. Modifying the returned list will not affect the underlying data + * structure of the ArgumentMultimap. */ public List getAllValues(Prefix prefix) { if (!argMultimap.containsKey(prefix)) { diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/vimification/logic/parser/ArgumentTokenizer.java similarity index 64% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/vimification/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..0b41a24a013 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/vimification/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; import java.util.ArrayList; import java.util.Arrays; @@ -7,21 +7,25 @@ /** * Tokenizes arguments string of the form: {@code preamble value value ...}
- * e.g. {@code some preamble text t/ 11.00 t/12.00 k/ m/ July} where prefixes are {@code t/ k/ m/}.
- * 1. An argument's value can be an empty string e.g. the value of {@code k/} in the above example.
+ * e.g. {@code some preamble text t/ 11.00 t/12.00 k/ m/ July} where prefixes are + * {@code t/ k/ m/}.
+ * 1. An argument's value can be an empty string e.g. the value of {@code k/} in the above + * example.
* 2. Leading and trailing whitespaces of an argument value will be discarded.
- * 3. An argument may be repeated and all its values will be accumulated e.g. the value of {@code t/} - * in the above example.
+ * 3. An argument may be repeated and all its values will be accumulated e.g. the value of + * {@code t/} in the above example.
*/ public class ArgumentTokenizer { /** - * Tokenizes an arguments string and returns an {@code ArgumentMultimap} object that maps prefixes to their - * respective argument values. Only the given prefixes will be recognized in the arguments string. + * Tokenizes an arguments string and returns an {@code ArgumentMultimap} object that maps + * prefixes to their respective argument values. Only the given prefixes will be recognized in + * the arguments string. * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixes Prefixes to tokenize the arguments string with - * @return ArgumentMultimap object that maps prefixes to their arguments + * @param argsString Arguments string of the form: + * {@code preamble value value ...} + * @param prefixes Prefixes to tokenize the arguments string with + * @return ArgumentMultimap object that maps prefixes to their arguments */ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) { List positions = findAllPrefixPositions(argsString, prefixes); @@ -31,11 +35,13 @@ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) { /** * Finds all zero-based prefix positions in the given arguments string. * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixes Prefixes to find in the arguments string - * @return List of zero-based prefix positions in the given arguments string + * @param argsString Arguments string of the form: + * {@code preamble value value ...} + * @param prefixes Prefixes to find in the arguments string + * @return List of zero-based prefix positions in the given arguments string */ - private static List findAllPrefixPositions(String argsString, Prefix... prefixes) { + private static List findAllPrefixPositions(String argsString, + Prefix... prefixes) { return Arrays.stream(prefixes) .flatMap(prefix -> findPrefixPositions(argsString, prefix).stream()) .collect(Collectors.toList()); @@ -58,16 +64,14 @@ private static List findPrefixPositions(String argsString, Prefi } /** - * Returns the index of the first occurrence of {@code prefix} in - * {@code argsString} starting from index {@code fromIndex}. An occurrence - * is valid if there is a whitespace before {@code prefix}. Returns -1 if no - * such occurrence can be found. + * Returns the index of the first occurrence of {@code prefix} in {@code argsString} starting + * from index {@code fromIndex}. An occurrence is valid if there is a whitespace before + * {@code prefix}. Returns -1 if no such occurrence can be found. * - * E.g if {@code argsString} = "e/hip/900", {@code prefix} = "p/" and - * {@code fromIndex} = 0, this method returns -1 as there are no valid - * occurrences of "p/" with whitespace before it. However, if - * {@code argsString} = "e/hi p/900", {@code prefix} = "p/" and - * {@code fromIndex} = 0, this method returns 5. + * E.g if {@code argsString} = "e/hip/900", {@code prefix} = "p/" and {@code fromIndex} = 0, + * this method returns -1 as there are no valid occurrences of "p/" with whitespace before it. + * However, if {@code argsString} = "e/hi p/900", {@code prefix} = "p/" and {@code fromIndex} = + * 0, this method returns 5. */ private static int findPrefixPosition(String argsString, String prefix, int fromIndex) { int prefixIndex = argsString.indexOf(" " + prefix, fromIndex); @@ -76,18 +80,21 @@ private static int findPrefixPosition(String argsString, String prefix, int from } /** - * Extracts prefixes and their argument values, and returns an {@code ArgumentMultimap} object that maps the - * extracted prefixes to their respective arguments. Prefixes are extracted based on their zero-based positions in - * {@code argsString}. + * Extracts prefixes and their argument values, and returns an {@code ArgumentMultimap} object + * that maps the extracted prefixes to their respective arguments. Prefixes are extracted based + * on their zero-based positions in {@code argsString}. * - * @param argsString Arguments string of the form: {@code preamble value value ...} + * @param argsString Arguments string of the form: + * {@code preamble value value ...} * @param prefixPositions Zero-based positions of all prefixes in {@code argsString} - * @return ArgumentMultimap object that maps prefixes to their arguments + * @return ArgumentMultimap object that maps prefixes to their arguments */ - private static ArgumentMultimap extractArguments(String argsString, List prefixPositions) { + private static ArgumentMultimap extractArguments(String argsString, + List prefixPositions) { // Sort by start position - prefixPositions.sort((prefix1, prefix2) -> prefix1.getStartPosition() - prefix2.getStartPosition()); + prefixPositions.sort( + (prefix1, prefix2) -> prefix1.getStartPosition() - prefix2.getStartPosition()); // Insert a PrefixPosition to represent the preamble PrefixPosition preambleMarker = new PrefixPosition(new Prefix(""), 0); @@ -102,7 +109,8 @@ private static ArgumentMultimap extractArguments(String argsString, List { /** - * Parses the given {@code String} of arguments in the context of the DeleteCommand - * and returns a DeleteCommand object for execution. + * Parses the given {@code String} of arguments in the context of the DeleteCommand and returns + * a DeleteCommand object for execution. + * * @throws ParseException if the user input does not conform the expected format */ public DeleteCommand parse(String args) throws ParseException { diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/vimification/logic/parser/EditCommandParser.java similarity index 52% rename from src/main/java/seedu/address/logic/parser/EditCommandParser.java rename to src/main/java/vimification/logic/parser/EditCommandParser.java index 845644b7dea..aad60e36afd 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/vimification/logic/parser/EditCommandParser.java @@ -1,23 +1,23 @@ -package seedu.address.logic.parser; +package vimification.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_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -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 vimification.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static vimification.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static vimification.logic.parser.CliSyntax.PREFIX_EMAIL; +import static vimification.logic.parser.CliSyntax.PREFIX_NAME; +import static vimification.logic.parser.CliSyntax.PREFIX_PHONE; +import static vimification.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Collection; import java.util.Collections; import java.util.Optional; import java.util.Set; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tag.Tag; +import vimification.commons.core.index.Index; +import vimification.logic.commands.EditCommand; +import vimification.logic.commands.EditCommand.EditPersonDescriptor; +import vimification.logic.parser.exceptions.ParseException; +import vimification.model.tag.Tag; /** * Parses input arguments and creates a new EditCommand object @@ -25,37 +25,45 @@ public class EditCommandParser implements Parser { /** - * Parses the given {@code String} of arguments in the context of the EditCommand - * and returns an EditCommand object for execution. + * Parses the given {@code String} of arguments in the context of the EditCommand and returns an + * EditCommand object for execution. + * * @throws ParseException if the user input does not conform the expected format */ public EditCommand parse(String args) throws ParseException { requireNonNull(args); ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, + PREFIX_ADDRESS, PREFIX_TAG); Index index; try { index = ParserUtil.parseIndex(argMultimap.getPreamble()); } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); } EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + editPersonDescriptor + .setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); } if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); + editPersonDescriptor + .setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); } if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { - editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + editPersonDescriptor + .setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); } if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + editPersonDescriptor.setAddress( + ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)) + .ifPresent(editPersonDescriptor::setTags); if (!editPersonDescriptor.isAnyFieldEdited()) { throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); @@ -75,7 +83,8 @@ private Optional> parseTagsForEdit(Collection tags) throws Pars if (tags.isEmpty()) { return Optional.empty(); } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + Collection tagSet = + tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; return Optional.of(ParserUtil.parseTags(tagSet)); } diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/vimification/logic/parser/FindCommandParser.java similarity index 67% rename from src/main/java/seedu/address/logic/parser/FindCommandParser.java rename to src/main/java/vimification/logic/parser/FindCommandParser.java index 4fb71f23103..54c250ea362 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/vimification/logic/parser/FindCommandParser.java @@ -1,12 +1,12 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static vimification.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import java.util.Arrays; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import vimification.logic.commands.FindCommand; +import vimification.logic.parser.exceptions.ParseException; +import vimification.model.person.NameContainsKeywordsPredicate; /** * Parses input arguments and creates a new FindCommand object @@ -14,8 +14,9 @@ public class FindCommandParser implements Parser { /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. + * Parses the given {@code String} of arguments in the context of the FindCommand and returns a + * FindCommand object for execution. + * * @throws ParseException if the user input does not conform the expected format */ public FindCommand parse(String args) throws ParseException { diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/vimification/logic/parser/Parser.java similarity index 71% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/vimification/logic/parser/Parser.java index d6551ad8e3f..f67dc174e19 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/vimification/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import vimification.logic.commands.Command; +import vimification.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. @@ -10,6 +10,7 @@ public interface Parser { /** * Parses {@code userInput} into a command and returns it. + * * @throws ParseException if {@code userInput} does not conform the expected format */ T parse(String userInput) throws ParseException; diff --git a/src/main/java/vimification/logic/parser/ParserException.java b/src/main/java/vimification/logic/parser/ParserException.java new file mode 100644 index 00000000000..e3083afd90d --- /dev/null +++ b/src/main/java/vimification/logic/parser/ParserException.java @@ -0,0 +1,21 @@ +package vimification.logic.parser; + +/** + * Exception that occurs when a string input cannot be parsed. + */ +public class ParserException extends RuntimeException { + + /** + * Constructs a default instance. + */ + public ParserException() {} + + /** + * Constructs an instance with a specific error message. + * + * @param message the error message + */ + public ParserException(String message) { + super(message); + } +} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/vimification/logic/parser/ParserUtil.java similarity index 76% rename from src/main/java/seedu/address/logic/parser/ParserUtil.java rename to src/main/java/vimification/logic/parser/ParserUtil.java index b117acb9c55..dfe1b2896cf 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/vimification/logic/parser/ParserUtil.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; import static java.util.Objects.requireNonNull; @@ -6,14 +6,14 @@ import java.util.HashSet; import java.util.Set; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import vimification.commons.core.index.Index; +import vimification.commons.util.StringUtil; +import vimification.logic.parser.exceptions.ParseException; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Phone; +import vimification.model.tag.Tag; /** * Contains utility methods used for parsing strings in the various *Parser classes. @@ -23,8 +23,9 @@ public class ParserUtil { public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; /** - * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be - * trimmed. + * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing + * whitespaces will be trimmed. + * * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). */ public static Index parseIndex(String oneBasedIndex) throws ParseException { @@ -36,8 +37,8 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException { } /** - * Parses a {@code String name} into a {@code Name}. - * Leading and trailing whitespaces will be trimmed. + * Parses a {@code String name} into a {@code Name}. Leading and trailing whitespaces will be + * trimmed. * * @throws ParseException if the given {@code name} is invalid. */ @@ -51,8 +52,8 @@ public static Name parseName(String name) throws ParseException { } /** - * Parses a {@code String phone} into a {@code Phone}. - * Leading and trailing whitespaces will be trimmed. + * Parses a {@code String phone} into a {@code Phone}. Leading and trailing whitespaces will be + * trimmed. * * @throws ParseException if the given {@code phone} is invalid. */ @@ -66,8 +67,8 @@ public static Phone parsePhone(String phone) throws ParseException { } /** - * Parses a {@code String address} into an {@code Address}. - * Leading and trailing whitespaces will be trimmed. + * Parses a {@code String address} into an {@code Address}. Leading and trailing whitespaces + * will be trimmed. * * @throws ParseException if the given {@code address} is invalid. */ @@ -81,8 +82,8 @@ public static Address parseAddress(String address) throws ParseException { } /** - * Parses a {@code String email} into an {@code Email}. - * Leading and trailing whitespaces will be trimmed. + * Parses a {@code String email} into an {@code Email}. Leading and trailing whitespaces will be + * trimmed. * * @throws ParseException if the given {@code email} is invalid. */ @@ -96,8 +97,8 @@ public static Email parseEmail(String email) throws ParseException { } /** - * Parses a {@code String tag} into a {@code Tag}. - * Leading and trailing whitespaces will be trimmed. + * Parses a {@code String tag} into a {@code Tag}. Leading and trailing whitespaces will be + * trimmed. * * @throws ParseException if the given {@code tag} is invalid. */ diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/vimification/logic/parser/Prefix.java similarity index 89% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/vimification/logic/parser/Prefix.java index c859d5fa5db..202a8c8613a 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/vimification/logic/parser/Prefix.java @@ -1,8 +1,8 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; /** - * A prefix that marks the beginning of an argument in an arguments string. - * E.g. 't/' in 'add James t/ friend'. + * A prefix that marks the beginning of an argument in an arguments string. E.g. 't/' in 'add James + * t/ friend'. */ public class Prefix { private final String prefix; diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/vimification/logic/parser/exceptions/ParseException.java similarity index 73% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/vimification/logic/parser/exceptions/ParseException.java index 158a1a54c1c..36122f69f5c 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/vimification/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package vimification.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import vimification.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/vimification/model/AddressBook.java similarity index 71% rename from src/main/java/seedu/address/model/AddressBook.java rename to src/main/java/vimification/model/AddressBook.java index 1a943a0781a..08f81c6a263 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/vimification/model/AddressBook.java @@ -1,27 +1,27 @@ -package seedu.address.model; +package vimification.model; import static java.util.Objects.requireNonNull; import java.util.List; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; +import vimification.model.person.Person; +import vimification.model.person.UniquePersonList; /** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) + * Wraps all data at the address-book level 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 + * 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. + * Note that non-static init blocks are not recommended to use. There are other ways to avoid + * duplication among constructors. */ { persons = new UniquePersonList(); @@ -40,8 +40,8 @@ public AddressBook(ReadOnlyAddressBook toBeCopied) { //// list overwrite operations /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. + * 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); @@ -67,8 +67,7 @@ public boolean hasPerson(Person person) { } /** - * Adds a person to the address book. - * The person must not already exist in the address book. + * Adds a person to the address book. The person must not already exist in the address book. */ public void addPerson(Person p) { persons.add(p); @@ -76,8 +75,8 @@ public void addPerson(Person p) { /** * Replaces the given person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + * {@code target} must exist in the address book. The person identity of {@code editedPerson} + * must not be the same as another existing person in the address book. */ public void setPerson(Person target, Person editedPerson) { requireNonNull(editedPerson); @@ -86,8 +85,8 @@ public void setPerson(Person target, Person editedPerson) { } /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. + * Removes {@code key} from this {@code AddressBook}. {@code key} must exist in the address + * book. */ public void removePerson(Person key) { persons.remove(key); @@ -110,7 +109,7 @@ public ObservableList getPersonList() { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); + && persons.equals(((AddressBook) other).persons)); } @Override diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/vimification/model/Model.java similarity index 80% rename from src/main/java/seedu/address/model/Model.java rename to src/main/java/vimification/model/Model.java index d54df471c1f..86738619eec 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/vimification/model/Model.java @@ -1,11 +1,11 @@ -package seedu.address.model; +package vimification.model; import java.nio.file.Path; import java.util.function.Predicate; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; +import vimification.commons.core.GuiSettings; +import vimification.model.person.Person; /** * The API of the Model component. @@ -58,21 +58,19 @@ public interface Model { boolean hasPerson(Person person); /** - * Deletes the given person. - * The person must exist in the address book. + * Deletes the given person. The person must exist in the address book. */ void deletePerson(Person target); /** - * Adds the given person. - * {@code person} must not already exist in the address book. + * Adds the given person. {@code person} must not already exist in the address book. */ void addPerson(Person person); /** - * Replaces the given person {@code target} with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + * Replaces the given person {@code target} with {@code editedPerson}. {@code target} must exist + * in the address book. The person identity of {@code editedPerson} must not be the same as + * another existing person in the address book. */ void setPerson(Person target, Person editedPerson); @@ -81,6 +79,7 @@ public interface Model { /** * Updates the filter of the filtered person list to filter by the given {@code predicate}. + * * @throws NullPointerException if {@code predicate} is null. */ void updateFilteredPersonList(Predicate predicate); diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/vimification/model/ModelManager.java similarity index 84% rename from src/main/java/seedu/address/model/ModelManager.java rename to src/main/java/vimification/model/ModelManager.java index 86c1df298d7..fd30101f299 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/vimification/model/ModelManager.java @@ -1,7 +1,7 @@ -package seedu.address.model; +package vimification.model; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static vimification.commons.util.CollectionUtil.requireAllNonNull; import java.nio.file.Path; import java.util.function.Predicate; @@ -9,9 +9,9 @@ import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import vimification.commons.core.GuiSettings; +import vimification.commons.core.LogsCenter; +import vimification.model.person.Person; /** * Represents the in-memory model of the address book data. @@ -29,7 +29,8 @@ public class ModelManager implements Model { public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { requireAllNonNull(addressBook, userPrefs); - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); + logger.fine( + "Initializing with address book: " + addressBook + " and user prefs " + userPrefs); this.addressBook = new AddressBook(addressBook); this.userPrefs = new UserPrefs(userPrefs); @@ -40,7 +41,8 @@ public ModelManager() { this(new AddressBook(), new UserPrefs()); } - //=========== UserPrefs ================================================================================== + // =========== UserPrefs + // ================================================================================== @Override public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { @@ -75,7 +77,8 @@ public void setAddressBookFilePath(Path addressBookFilePath) { userPrefs.setAddressBookFilePath(addressBookFilePath); } - //=========== AddressBook ================================================================================ + // =========== AddressBook + // ================================================================================ @Override public void setAddressBook(ReadOnlyAddressBook addressBook) { @@ -111,7 +114,8 @@ public void setPerson(Person target, Person editedPerson) { addressBook.setPerson(target, editedPerson); } - //=========== Filtered Person List Accessors ============================================================= + // =========== Filtered Person List Accessors + // ============================================================= /** * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/vimification/model/ReadOnlyAddressBook.java similarity index 51% rename from src/main/java/seedu/address/model/ReadOnlyAddressBook.java rename to src/main/java/vimification/model/ReadOnlyAddressBook.java index 6ddc2cd9a29..fdff0254a8a 100644 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ b/src/main/java/vimification/model/ReadOnlyAddressBook.java @@ -1,7 +1,7 @@ -package seedu.address.model; +package vimification.model; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; +import vimification.model.person.Person; /** * Unmodifiable view of an address book @@ -9,8 +9,8 @@ public interface ReadOnlyAddressBook { /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. + * Returns an unmodifiable view of the persons list. This list will not contain any duplicate + * persons. */ ObservableList getPersonList(); diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/vimification/model/ReadOnlyUserPrefs.java similarity index 70% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/vimification/model/ReadOnlyUserPrefs.java index befd58a4c73..ab1cf5731d6 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/vimification/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package vimification.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import vimification.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/vimification/model/UserPrefs.java similarity index 90% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/vimification/model/UserPrefs.java index 25a5fd6eab9..3a770d6513e 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/vimification/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package vimification.model; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Objects; -import seedu.address.commons.core.GuiSettings; +import vimification.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path addressBookFilePath = Paths.get("data", "addressbook.json"); /** * Creates a {@code UserPrefs} with default values. @@ -61,7 +61,7 @@ public boolean equals(Object other) { if (other == this) { return true; } - if (!(other instanceof UserPrefs)) { //this handles null as well. + if (!(other instanceof UserPrefs)) { // this handles null as well. return false; } diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/vimification/model/person/Address.java similarity index 67% rename from src/main/java/seedu/address/model/person/Address.java rename to src/main/java/vimification/model/person/Address.java index 60472ca22a0..2800d5fde0d 100644 --- a/src/main/java/seedu/address/model/person/Address.java +++ b/src/main/java/vimification/model/person/Address.java @@ -1,19 +1,20 @@ -package seedu.address.model.person; +package vimification.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static vimification.commons.util.AppUtil.checkArgument; /** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} + * Represents a Person's address in the address book. Guarantees: immutable; is valid as declared in + * {@link #isValidAddress(String)} */ public class Address { - public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank"; + public static final String MESSAGE_CONSTRAINTS = + "Addresses can take any values, and it should not be blank"; /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. + * The first character of the address must not be a whitespace, otherwise " " (a blank string) + * becomes a valid input. */ public static final String VALIDATION_REGEX = "[^\\s].*"; @@ -46,7 +47,7 @@ public String toString() { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check + && value.equals(((Address) other).value)); // state check } @Override diff --git a/src/main/java/vimification/model/person/Email.java b/src/main/java/vimification/model/person/Email.java new file mode 100644 index 00000000000..2fa1ee2a1f3 --- /dev/null +++ b/src/main/java/vimification/model/person/Email.java @@ -0,0 +1,79 @@ +package vimification.model.person; + +import static java.util.Objects.requireNonNull; +import static vimification.commons.util.AppUtil.checkArgument; + +/** + * Represents a Person's email in the address book. Guarantees: immutable; is valid as declared in + * {@link #isValidEmail(String)} + */ +public class Email { + + private static final String SPECIAL_CHARACTERS = "+_.-"; + public static final String MESSAGE_CONSTRAINTS = + "Emails should be of the format local-part@domain " + + "and adhere to the following constraints:\n" + + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " + + "the parentheses, (" + SPECIAL_CHARACTERS + + "). The local-part may not start or end with any special " + + "characters.\n" + + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels " + + "separated by periods.\n" + + "The domain name must:\n" + + " - end with a domain label at least 2 characters long\n" + + " - have each domain label start and end with alphanumeric characters\n" + + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any."; + // alphanumeric and special characters + private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters + // except underscore + private static final String LOCAL_PART_REGEX = + "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]" + + ALPHANUMERIC_NO_UNDERSCORE + ")*"; + private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE + + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*"; + private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At + // least + // two + // chars + private static final String DOMAIN_REGEX = + "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX; + public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX; + + public final String value; + + /** + * Constructs an {@code Email}. + * + * @param email A valid email address. + */ + public Email(String email) { + requireNonNull(email); + checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); + value = email; + } + + /** + * Returns if a given string is a valid email. + */ + public static boolean isValidEmail(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Email // instanceof handles nulls + && value.equals(((Email) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/vimification/model/person/Name.java similarity index 76% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/vimification/model/person/Name.java index 79244d71cf7..afce9d7d3f4 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/vimification/model/person/Name.java @@ -1,11 +1,11 @@ -package seedu.address.model.person; +package vimification.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static vimification.commons.util.AppUtil.checkArgument; /** - * Represents a Person's name in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} + * Represents a Person's name in the address book. Guarantees: immutable; is valid as declared in + * {@link #isValidName(String)} */ public class Name { @@ -13,8 +13,8 @@ public class Name { "Names should only contain alphanumeric characters and spaces, and it should not be blank"; /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. + * The first character of the address must not be a whitespace, otherwise " " (a blank string) + * becomes a valid input. */ public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; @@ -48,7 +48,7 @@ public String toString() { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check + && fullName.equals(((Name) other).fullName)); // state check } @Override diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/vimification/model/person/NameContainsKeywordsPredicate.java similarity index 67% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/vimification/model/person/NameContainsKeywordsPredicate.java index c9b5868427c..de53f924724 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/vimification/model/person/NameContainsKeywordsPredicate.java @@ -1,9 +1,9 @@ -package seedu.address.model.person; +package vimification.model.person; import java.util.List; import java.util.function.Predicate; -import seedu.address.commons.util.StringUtil; +import vimification.commons.util.StringUtil; /** * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. @@ -18,14 +18,16 @@ public NameContainsKeywordsPredicate(List keywords) { @Override public boolean test(Person person) { return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, + keyword)); } @Override public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof NameContainsKeywordsPredicate // instanceof handles nulls - && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state check + && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state + // check } } diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/vimification/model/person/Person.java similarity index 83% rename from src/main/java/seedu/address/model/person/Person.java rename to src/main/java/vimification/model/person/Person.java index 8ff1d83fe89..fc0c46d635a 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/vimification/model/person/Person.java @@ -1,17 +1,17 @@ -package seedu.address.model.person; +package vimification.model.person; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static vimification.commons.util.CollectionUtil.requireAllNonNull; import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.Set; -import seedu.address.model.tag.Tag; +import vimification.model.tag.Tag; /** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. + * Represents a Person in the address book. Guarantees: details are present and not null, field + * values are validated, immutable. */ public class Person { @@ -53,16 +53,16 @@ public Address getAddress() { } /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} if + * modification is attempted. */ public Set getTags() { return Collections.unmodifiableSet(tags); } /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. + * Returns true if both persons have the same name. This defines a weaker notion of equality + * between two persons. */ public boolean isSamePerson(Person otherPerson) { if (otherPerson == this) { @@ -74,8 +74,8 @@ public boolean isSamePerson(Person otherPerson) { } /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. + * Returns true if both persons have the same identity and data fields. This defines a stronger + * notion of equality between two persons. */ @Override public boolean equals(Object other) { diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/vimification/model/person/Phone.java similarity index 78% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/vimification/model/person/Phone.java index 872c76b382f..08c3a97a49b 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/vimification/model/person/Phone.java @@ -1,11 +1,11 @@ -package seedu.address.model.person; +package vimification.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static vimification.commons.util.AppUtil.checkArgument; /** - * Represents a Person's phone number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} + * Represents a Person's phone number in the address book. Guarantees: immutable; is valid as + * declared in {@link #isValidPhone(String)} */ public class Phone { @@ -42,7 +42,7 @@ public String toString() { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof Phone // instanceof handles nulls - && value.equals(((Phone) other).value)); // state check + && value.equals(((Phone) other).value)); // state check } @Override diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/vimification/model/person/UniquePersonList.java similarity index 73% rename from src/main/java/seedu/address/model/person/UniquePersonList.java rename to src/main/java/vimification/model/person/UniquePersonList.java index 0fee4fe57e6..6e861f16a2a 100644 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ b/src/main/java/vimification/model/person/UniquePersonList.java @@ -1,22 +1,23 @@ -package seedu.address.model.person; +package vimification.model.person; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static vimification.commons.util.CollectionUtil.requireAllNonNull; import java.util.Iterator; import java.util.List; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; +import vimification.model.person.exceptions.DuplicatePersonException; +import vimification.model.person.exceptions.PersonNotFoundException; /** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. + * A list of persons that enforces uniqueness between its elements and does not allow nulls. A + * person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, + * adding and updating of persons uses Person#isSamePerson(Person) for equality so as to ensure that + * the person being added or updated is unique in terms of identity in the UniquePersonList. + * However, the removal of a person uses Person#equals(Object) so as to ensure that the person with + * exactly the same fields will be removed. * * Supports a minimal set of list operations. * @@ -37,8 +38,7 @@ public boolean contains(Person toCheck) { } /** - * Adds a person to the list. - * The person must not already exist in the list. + * Adds a person to the list. The person must not already exist in the list. */ public void add(Person toAdd) { requireNonNull(toAdd); @@ -49,9 +49,9 @@ public void add(Person toAdd) { } /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. + * Replaces the person {@code target} in the list with {@code editedPerson}. {@code target} must + * exist in the list. The person identity of {@code editedPerson} must not be the same as + * another existing person in the list. */ public void setPerson(Person target, Person editedPerson) { requireAllNonNull(target, editedPerson); @@ -69,8 +69,7 @@ public void setPerson(Person target, Person editedPerson) { } /** - * Removes the equivalent person from the list. - * The person must exist in the list. + * Removes the equivalent person from the list. The person must exist in the list. */ public void remove(Person toRemove) { requireNonNull(toRemove); @@ -85,8 +84,8 @@ public void setPersons(UniquePersonList replacement) { } /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. + * Replaces the contents of this list with {@code persons}. {@code persons} must not contain + * duplicate persons. */ public void setPersons(List persons) { requireAllNonNull(persons); diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/vimification/model/person/exceptions/DuplicatePersonException.java similarity index 67% rename from src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java rename to src/main/java/vimification/model/person/exceptions/DuplicatePersonException.java index d7290f59442..91c5e7dbd42 100644 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ b/src/main/java/vimification/model/person/exceptions/DuplicatePersonException.java @@ -1,8 +1,8 @@ -package seedu.address.model.person.exceptions; +package vimification.model.person.exceptions; /** - * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same - * identity). + * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if + * they have the same identity). */ public class DuplicatePersonException extends RuntimeException { public DuplicatePersonException() { diff --git a/src/main/java/vimification/model/person/exceptions/PersonNotFoundException.java b/src/main/java/vimification/model/person/exceptions/PersonNotFoundException.java new file mode 100644 index 00000000000..0b9c450b1ef --- /dev/null +++ b/src/main/java/vimification/model/person/exceptions/PersonNotFoundException.java @@ -0,0 +1,7 @@ +package vimification.model.person.exceptions; + +/** + * Signals that the operation is unable to find the specified person. + */ +public class PersonNotFoundException extends RuntimeException { +} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/vimification/model/tag/Tag.java similarity index 78% rename from src/main/java/seedu/address/model/tag/Tag.java rename to src/main/java/vimification/model/tag/Tag.java index b0ea7e7dad7..9789646dda2 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/vimification/model/tag/Tag.java @@ -1,11 +1,11 @@ -package seedu.address.model.tag; +package vimification.model.tag; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static vimification.commons.util.AppUtil.checkArgument; /** - * Represents a Tag in the address book. - * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} + * Represents a Tag in the address book. Guarantees: immutable; name is valid as declared in + * {@link #isValidTagName(String)} */ public class Tag { @@ -36,7 +36,7 @@ public static boolean isValidTagName(String test) { public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof Tag // instanceof handles nulls - && tagName.equals(((Tag) other).tagName)); // state check + && tagName.equals(((Tag) other).tagName)); // state check } @Override diff --git a/src/main/java/vimification/model/util/SampleDataUtil.java b/src/main/java/vimification/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..3959db5b144 --- /dev/null +++ b/src/main/java/vimification/model/util/SampleDataUtil.java @@ -0,0 +1,66 @@ +package vimification.model.util; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import vimification.model.AddressBook; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Person; +import vimification.model.person.Phone; +import vimification.model.tag.Tag; + +/** + * Contains utility methods for populating {@code AddressBook} with sample data. + */ +public class SampleDataUtil { + 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"), + 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"), + 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"), + 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"), + 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"), + 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"), + getTagSet("colleagues")) + }; + } + + public static ReadOnlyAddressBook getSampleAddressBook() { + AddressBook sampleAb = new AddressBook(); + for (Person samplePerson : getSamplePersons()) { + sampleAb.addPerson(samplePerson); + } + return sampleAb; + } + + /** + * Returns a tag set containing the list of strings given. + */ + public static Set getTagSet(String... strings) { + return Arrays.stream(strings) + .map(Tag::new) + .collect(Collectors.toSet()); + } + +} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/vimification/storage/AddressBookStorage.java similarity index 76% rename from src/main/java/seedu/address/storage/AddressBookStorage.java rename to src/main/java/vimification/storage/AddressBookStorage.java index 4599182b3f9..5af5286d1ae 100644 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ b/src/main/java/vimification/storage/AddressBookStorage.java @@ -1,14 +1,14 @@ -package seedu.address.storage; +package vimification.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; +import vimification.commons.exceptions.DataConversionException; +import vimification.model.ReadOnlyAddressBook; /** - * Represents a storage for {@link seedu.address.model.AddressBook}. + * Represents a storage for {@link vimification.model.AddressBook}. */ public interface AddressBookStorage { @@ -18,8 +18,9 @@ public interface AddressBookStorage { Path getAddressBookFilePath(); /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. - * Returns {@code Optional.empty()} if storage file is not found. + * Returns AddressBook data as a {@link ReadOnlyAddressBook}. Returns {@code Optional.empty()} + * if storage file is not found. + * * @throws DataConversionException if the data in storage is not in the expected format. * @throws IOException if there was any problem when reading from the storage. */ @@ -28,10 +29,12 @@ public interface AddressBookStorage { /** * @see #getAddressBookFilePath() */ - Optional readAddressBook(Path filePath) throws DataConversionException, IOException; + Optional readAddressBook(Path filePath) + throws DataConversionException, IOException; /** * Saves the given {@link ReadOnlyAddressBook} to the storage. + * * @param addressBook cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/vimification/storage/JsonAdaptedPerson.java similarity index 76% rename from src/main/java/seedu/address/storage/JsonAdaptedPerson.java rename to src/main/java/vimification/storage/JsonAdaptedPerson.java index a6321cec2ea..8f479ef5f7d 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/vimification/storage/JsonAdaptedPerson.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package vimification.storage; import java.util.ArrayList; import java.util.HashSet; @@ -9,13 +9,13 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import vimification.commons.exceptions.IllegalValueException; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Person; +import vimification.model.person.Phone; +import vimification.model.tag.Tag; /** * Jackson-friendly version of {@link Person}. @@ -62,7 +62,8 @@ public JsonAdaptedPerson(Person source) { /** * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. + * @throws IllegalValueException if there were any data constraints violated in the adapted + * person. */ public Person toModelType() throws IllegalValueException { final List personTags = new ArrayList<>(); @@ -71,7 +72,8 @@ public Person toModelType() throws IllegalValueException { } if (name == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); } if (!Name.isValidName(name)) { throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); @@ -79,7 +81,8 @@ public Person toModelType() throws IllegalValueException { final Name modelName = new Name(name); if (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); } if (!Phone.isValidPhone(phone)) { throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); @@ -87,7 +90,8 @@ public Person toModelType() throws IllegalValueException { final Phone modelPhone = new Phone(phone); if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); } if (!Email.isValidEmail(email)) { throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); @@ -95,7 +99,8 @@ public Person toModelType() throws IllegalValueException { final Email modelEmail = new Email(email); if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); + throw new IllegalValueException( + String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); } if (!Address.isValidAddress(address)) { throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/vimification/storage/JsonAdaptedTag.java similarity index 89% rename from src/main/java/seedu/address/storage/JsonAdaptedTag.java rename to src/main/java/vimification/storage/JsonAdaptedTag.java index 0df22bdb754..e5bc733580a 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ b/src/main/java/vimification/storage/JsonAdaptedTag.java @@ -1,10 +1,10 @@ -package seedu.address.storage; +package vimification.storage; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; +import vimification.commons.exceptions.IllegalValueException; +import vimification.model.tag.Tag; /** * Jackson-friendly version of {@link Tag}. diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/vimification/storage/JsonAddressBookStorage.java similarity index 85% rename from src/main/java/seedu/address/storage/JsonAddressBookStorage.java rename to src/main/java/vimification/storage/JsonAddressBookStorage.java index dfab9daaa0d..fef8959f3b0 100644 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ b/src/main/java/vimification/storage/JsonAddressBookStorage.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package vimification.storage; import static java.util.Objects.requireNonNull; @@ -7,12 +7,12 @@ import java.util.Optional; import java.util.logging.Logger; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.FileUtil; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyAddressBook; +import vimification.commons.core.LogsCenter; +import vimification.commons.exceptions.DataConversionException; +import vimification.commons.exceptions.IllegalValueException; +import vimification.commons.util.FileUtil; +import vimification.commons.util.JsonUtil; +import vimification.model.ReadOnlyAddressBook; /** * A class to access AddressBook data stored as a json file on the hard disk. @@ -42,7 +42,8 @@ public Optional readAddressBook() throws DataConversionExce * @param filePath location of the data. Cannot be null. * @throws DataConversionException if the file is not in the correct format. */ - public Optional readAddressBook(Path filePath) throws DataConversionException { + public Optional readAddressBook(Path filePath) + throws DataConversionException { requireNonNull(filePath); Optional jsonAddressBook = JsonUtil.readJsonFile( diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/vimification/storage/JsonSerializableAddressBook.java similarity index 78% rename from src/main/java/seedu/address/storage/JsonSerializableAddressBook.java rename to src/main/java/vimification/storage/JsonSerializableAddressBook.java index 5efd834091d..ffc257b8c13 100644 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ b/src/main/java/vimification/storage/JsonSerializableAddressBook.java @@ -1,4 +1,4 @@ -package seedu.address.storage; +package vimification.storage; import java.util.ArrayList; import java.util.List; @@ -8,10 +8,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonRootName; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import vimification.commons.exceptions.IllegalValueException; +import vimification.model.AddressBook; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.person.Person; /** * An Immutable AddressBook that is serializable to JSON format. @@ -19,7 +19,8 @@ @JsonRootName(value = "addressbook") class JsonSerializableAddressBook { - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; + public static final String MESSAGE_DUPLICATE_PERSON = + "Persons list contains duplicate person(s)."; private final List persons = new ArrayList<>(); @@ -34,10 +35,12 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List readUserPrefs() throws DataConversionException { /** * Similar to {@link #readUserPrefs()} + * * @param prefsFilePath location of the data. Cannot be null. * @throws DataConversionException if the file format is not as expected. */ diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/vimification/storage/Storage.java similarity index 73% rename from src/main/java/seedu/address/storage/Storage.java rename to src/main/java/vimification/storage/Storage.java index beda8bd9f11..0efcfec7b64 100644 --- a/src/main/java/seedu/address/storage/Storage.java +++ b/src/main/java/vimification/storage/Storage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package vimification.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import vimification.commons.exceptions.DataConversionException; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyUserPrefs; +import vimification.model.UserPrefs; /** * API of the Storage component diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/vimification/storage/StorageManager.java similarity index 79% rename from src/main/java/seedu/address/storage/StorageManager.java rename to src/main/java/vimification/storage/StorageManager.java index 6cfa0162164..dafae72ae6c 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/vimification/storage/StorageManager.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package vimification.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; import java.util.logging.Logger; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import vimification.commons.core.LogsCenter; +import vimification.commons.exceptions.DataConversionException; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyUserPrefs; +import vimification.model.UserPrefs; /** * Manages storage of AddressBook data in local storage. @@ -21,9 +21,11 @@ public class StorageManager implements Storage { private UserPrefsStorage userPrefsStorage; /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. + * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and + * {@code UserPrefStorage}. */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { + public StorageManager(AddressBookStorage addressBookStorage, + UserPrefsStorage userPrefsStorage) { this.addressBookStorage = addressBookStorage; this.userPrefsStorage = userPrefsStorage; } @@ -54,12 +56,14 @@ public Path getAddressBookFilePath() { } @Override - public Optional readAddressBook() throws DataConversionException, IOException { + public Optional readAddressBook() + throws DataConversionException, IOException { return readAddressBook(addressBookStorage.getAddressBookFilePath()); } @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { + public Optional readAddressBook(Path filePath) + throws DataConversionException, IOException { logger.fine("Attempting to read data from file: " + filePath); return addressBookStorage.readAddressBook(filePath); } diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/vimification/storage/UserPrefsStorage.java similarity index 61% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/vimification/storage/UserPrefsStorage.java index 29eef178dbc..30174fb5406 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/vimification/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package vimification.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import vimification.commons.exceptions.DataConversionException; +import vimification.model.ReadOnlyUserPrefs; +import vimification.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link vimification.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -19,15 +19,17 @@ public interface UserPrefsStorage { Path getUserPrefsFilePath(); /** - * Returns UserPrefs data from storage. - * Returns {@code Optional.empty()} if storage file is not found. + * Returns UserPrefs data from storage. Returns {@code Optional.empty()} if storage file is not + * found. + * * @throws DataConversionException if the data in storage is not in the expected format. * @throws IOException if there was any problem when reading from the storage. */ Optional readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * Saves the given {@link vimification.model.ReadOnlyUserPrefs} to the storage. + * * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/vimification/ui/CommandBox.java similarity index 85% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/vimification/ui/CommandBox.java index 9e75478664b..556133b187a 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/vimification/ui/CommandBox.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package vimification.ui; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TextField; import javafx.scene.layout.Region; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import vimification.logic.commands.CommandResult; +import vimification.logic.commands.exceptions.CommandException; +import vimification.logic.parser.exceptions.ParseException; /** * The UI component that is responsible for receiving user command inputs. @@ -28,7 +28,8 @@ public CommandBox(CommandExecutor commandExecutor) { super(FXML); this.commandExecutor = commandExecutor; // calls #setStyleToDefault() whenever there is a change to the text of the command box. - commandTextField.textProperty().addListener((unused1, unused2, unused3) -> setStyleToDefault()); + commandTextField.textProperty() + .addListener((unused1, unused2, unused3) -> setStyleToDefault()); } /** @@ -77,7 +78,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see vimification.logic.Logic#execute(String) */ CommandResult execute(String commandText) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/vimification/ui/HelpWindow.java similarity index 75% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/vimification/ui/HelpWindow.java index 3f16b2fcf26..830fe5f3fc3 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/vimification/ui/HelpWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package vimification.ui; import java.util.logging.Logger; @@ -8,14 +8,15 @@ import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import vimification.commons.core.LogsCenter; /** * Controller for a help page */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; + public static final String USERGUIDE_URL = + "https://se-education.org/addressbook-level3/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); @@ -46,21 +47,15 @@ public HelpWindow() { /** * Shows the help window. + * * @throws IllegalStateException - *

    - *
  • - * if this method is called on a thread other than the JavaFX Application Thread. + *
      + *
    • if this method is called on a thread other than the JavaFX Application Thread. *
    • - *
    • - * if this method is called during animation or layout processing. - *
    • - *
    • - * if this method is called on the primary stage. - *
    • - *
    • - * if {@code dialogStage} is already showing. - *
    • - *
    + *
  • if this method is called during animation or layout processing.
  • + *
  • if this method is called on the primary stage.
  • + *
  • if {@code dialogStage} is already showing.
  • + *
*/ public void show() { logger.fine("Showing help page about the application."); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/vimification/ui/MainWindow.java similarity index 82% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/vimification/ui/MainWindow.java index 9106c3aa6e5..2fa89b507d8 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/vimification/ui/MainWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package vimification.ui; import java.util.logging.Logger; @@ -10,16 +10,16 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; import javafx.stage.Stage; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.logic.Logic; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; +import vimification.commons.core.GuiSettings; +import vimification.commons.core.LogsCenter; +import vimification.logic.Logic; +import vimification.logic.commands.CommandResult; +import vimification.logic.commands.exceptions.CommandException; +import vimification.logic.parser.exceptions.ParseException; /** - * The Main Window. Provides the basic application layout containing - * a menu bar and space where other JavaFX elements can be placed. + * The Main Window. Provides the basic application layout containing a menu bar and space where + * other JavaFX elements can be placed. */ public class MainWindow extends UiPart { @@ -78,6 +78,7 @@ private void setAccelerators() { /** * Sets the accelerator of a MenuItem. + * * @param keyCombination the KeyCombination value of the accelerator */ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { @@ -85,18 +86,15 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { /* * TODO: the code below can be removed once the bug reported here - * https://bugs.openjdk.java.net/browse/JDK-8131666 - * is fixed in later version of SDK. + * https://bugs.openjdk.java.net/browse/JDK-8131666 is fixed in later version of SDK. * - * According to the bug report, TextInputControl (TextField, TextArea) will - * consume function-key events. Because CommandBox contains a TextField, and - * ResultDisplay contains a TextArea, thus some accelerators (e.g F1) will - * not work when the focus is in them because the key event is consumed by - * the TextInputControl(s). + * According to the bug report, TextInputControl (TextField, TextArea) will consume + * function-key events. Because CommandBox contains a TextField, and ResultDisplay contains + * a TextArea, thus some accelerators (e.g F1) will not work when the focus is in them + * because the key event is consumed by the TextInputControl(s). * - * For now, we add following event filter to capture such key events and open - * help window purposely so to support accelerators even when focus is - * in CommandBox or ResultDisplay. + * For now, we add following event filter to capture such key events and open help window + * purposely so to support accelerators even when focus is in CommandBox or ResultDisplay. */ getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> { if (event.getTarget() instanceof TextInputControl && keyCombination.match(event)) { @@ -170,9 +168,10 @@ public PersonListPanel getPersonListPanel() { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see vimification.logic.Logic#execute(String) */ - private CommandResult executeCommand(String commandText) throws CommandException, ParseException { + private CommandResult executeCommand(String commandText) + throws CommandException, ParseException { try { CommandResult commandResult = logic.execute(commandText); logger.info("Result: " + commandResult.getFeedbackToUser()); diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/vimification/ui/PersonCard.java similarity index 86% rename from src/main/java/seedu/address/ui/PersonCard.java rename to src/main/java/vimification/ui/PersonCard.java index 7fc927bc5d9..cb05d3b2b3b 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/vimification/ui/PersonCard.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package vimification.ui; import java.util.Comparator; @@ -7,7 +7,7 @@ import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; -import seedu.address.model.person.Person; +import vimification.model.person.Person; /** * An UI component that displays information of a {@code Person}. @@ -17,11 +17,12 @@ public class PersonCard extends UiPart { private static final String FXML = "PersonListCard.fxml"; /** - * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. - * As a consequence, UI elements' variable names cannot be set to such keywords - * or an exception will be thrown by JavaFX during runtime. + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. As + * a consequence, UI elements' variable names cannot be set to such keywords or an exception + * will be thrown by JavaFX during runtime. * - * @see
The issue on AddressBook level 4 + * @see The issue on + * AddressBook level 4 */ public final Person person; diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/vimification/ui/PersonListPanel.java similarity index 89% rename from src/main/java/seedu/address/ui/PersonListPanel.java rename to src/main/java/vimification/ui/PersonListPanel.java index f4c501a897b..61dc9d353bf 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/vimification/ui/PersonListPanel.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package vimification.ui; import java.util.logging.Logger; @@ -7,8 +7,8 @@ import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import vimification.commons.core.LogsCenter; +import vimification.model.person.Person; /** * Panel containing the list of persons. @@ -30,7 +30,8 @@ public PersonListPanel(ObservableList personList) { } /** - * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. + * Custom {@code ListCell} that displays the graphics of a {@code Person} using a + * {@code PersonCard}. */ class PersonListViewCell extends ListCell { @Override diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/vimification/ui/ResultDisplay.java similarity index 95% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/vimification/ui/ResultDisplay.java index 7d98e84eedf..8a79ea44dec 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/vimification/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package vimification.ui; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/vimification/ui/StatusBarFooter.java similarity index 96% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/vimification/ui/StatusBarFooter.java index b577f829423..d8c7bf61e3d 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/vimification/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package vimification.ui; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/vimification/ui/Ui.java similarity index 64% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/vimification/ui/Ui.java index 17aa0b494fe..8daf7837d4c 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/vimification/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package vimification.ui; import javafx.stage.Stage; @@ -7,7 +7,7 @@ */ public interface Ui { - /** Starts the UI (and the App). */ + /** Starts the UI (and the App). */ void start(Stage primaryStage); } diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/vimification/ui/UiManager.java similarity index 78% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/vimification/ui/UiManager.java index fdf024138bc..2e4c5299dde 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/vimification/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package vimification.ui; import java.util.logging.Logger; @@ -7,10 +7,10 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.stage.Stage; -import seedu.address.MainApp; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; +import vimification.MainApp; +import vimification.commons.core.LogsCenter; +import vimification.commons.util.StringUtil; +import vimification.logic.Logic; /** * The manager of the UI component. @@ -36,12 +36,12 @@ public UiManager(Logic logic) { public void start(Stage primaryStage) { logger.info("Starting UI..."); - //Set the application icon. + // Set the application icon. primaryStage.getIcons().add(getImage(ICON_APPLICATION)); try { mainWindow = new MainWindow(primaryStage, logic); - mainWindow.show(); //This should be called before creating other UI parts + mainWindow.show(); // This should be called before creating other UI parts mainWindow.fillInnerParts(); } catch (Throwable e) { @@ -54,16 +54,18 @@ private Image getImage(String imagePath) { return new Image(MainApp.class.getResourceAsStream(imagePath)); } - void showAlertDialogAndWait(Alert.AlertType type, String title, String headerText, String contentText) { + void showAlertDialogAndWait(Alert.AlertType type, String title, String headerText, + String contentText) { showAlertDialogAndWait(mainWindow.getPrimaryStage(), type, title, headerText, contentText); } /** - * Shows an alert dialog on {@code owner} with the given parameters. - * This method only returns after the user has closed the alert dialog. + * Shows an alert dialog on {@code owner} with the given parameters. This method only returns + * after the user has closed the alert dialog. */ - private static void showAlertDialogAndWait(Stage owner, AlertType type, String title, String headerText, - String contentText) { + private static void showAlertDialogAndWait(Stage owner, AlertType type, String title, + String headerText, + String contentText) { final Alert alert = new Alert(type); alert.getDialogPane().getStylesheets().add("view/DarkTheme.css"); alert.initOwner(owner); @@ -75,8 +77,8 @@ private static void showAlertDialogAndWait(Stage owner, AlertType type, String t } /** - * Shows an error alert dialog with {@code title} and error message, {@code e}, - * and exits the application after the user has closed the alert dialog. + * Shows an error alert dialog with {@code title} and error message, {@code e}, and exits the + * application after the user has closed the alert dialog. */ private void showFatalErrorDialogAndShutdown(String title, Throwable e) { logger.severe(title + " " + e.getMessage() + StringUtil.getDetails(e)); diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/vimification/ui/UiPart.java similarity index 83% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/vimification/ui/UiPart.java index fc820e01a9c..fb73c044562 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/vimification/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package vimification.ui; import static java.util.Objects.requireNonNull; @@ -6,11 +6,11 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import vimification.MainApp; /** - * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. - * It contains a scene graph with a root node of type {@code T}. + * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. It + * contains a scene graph with a root node of type {@code T}. */ public abstract class UiPart { @@ -20,8 +20,8 @@ public abstract class UiPart { private final FXMLLoader fxmlLoader = new FXMLLoader(); /** - * Constructs a UiPart with the specified FXML file URL. - * The FXML file must not specify the {@code fx:controller} attribute. + * Constructs a UiPart with the specified FXML file URL. The FXML file must not specify the + * {@code fx:controller} attribute. */ public UiPart(URL fxmlFileUrl) { loadFxmlFile(fxmlFileUrl, null); @@ -29,6 +29,7 @@ public UiPart(URL fxmlFileUrl) { /** * Constructs a UiPart using the specified FXML file within {@link #FXML_FILE_FOLDER}. + * * @see #UiPart(URL) */ public UiPart(String fxmlFileName) { @@ -36,15 +37,17 @@ public UiPart(String fxmlFileName) { } /** - * Constructs a UiPart with the specified FXML file URL and root object. - * The FXML file must not specify the {@code fx:controller} attribute. + * Constructs a UiPart with the specified FXML file URL and root object. The FXML file must not + * specify the {@code fx:controller} attribute. */ public UiPart(URL fxmlFileUrl, T root) { loadFxmlFile(fxmlFileUrl, root); } /** - * Constructs a UiPart with the specified FXML file within {@link #FXML_FILE_FOLDER} and root object. + * Constructs a UiPart with the specified FXML file within {@link #FXML_FILE_FOLDER} and root + * object. + * * @see #UiPart(URL, T) */ public UiPart(String fxmlFileName, T root) { @@ -60,6 +63,7 @@ public T getRoot() { /** * Loads the object hierarchy from a FXML document. + * * @param location Location of the FXML document. * @param root Specifies the root of the object hierarchy. */ diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java deleted file mode 100644 index 9533c473875..00000000000 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; - -public class ExitCommandTest { - private Model model = new ModelManager(); - private Model expectedModel = new ModelManager(); - - @Test - public void execute_exit_success() { - CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); - assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel); - } -} diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java deleted file mode 100644 index 5cf487d7ebb..00000000000 --- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalPersons.AMY; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.PersonBuilder; - -public class AddCommandParserTest { - private AddCommandParser parser = new AddCommandParser(); - - @Test - public void parse_allFieldsPresent_success() { - Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); - - // whitespace only preamble - assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple names - last name accepted - assertParseSuccess(parser, NAME_DESC_AMY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple phones - last phone accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_AMY + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple emails - last email accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple addresses - last address accepted - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_AMY - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple tags - all accepted - Person expectedPersonMultipleTags = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); - assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddCommand(expectedPersonMultipleTags)); - } - - @Test - public void parse_optionalFieldsMissing_success() { - // zero tags - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); - assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY, - new AddCommand(expectedPerson)); - } - - @Test - public void parse_compulsoryFieldMissing_failure() { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); - - // missing name prefix - assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing phone prefix - assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing email prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing address prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB, - expectedMessage); - - // all prefixes missing - assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB, - expectedMessage); - } - - @Test - public void parse_invalidValue_failure() { - // invalid name - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS); - - // invalid phone - assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS); - - // invalid email - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS); - - // invalid address - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS); - - // invalid tag - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS); - - // two invalid values, only first invalid value reported - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC, - Name.MESSAGE_CONSTRAINTS); - - // non-empty preamble - assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, - String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } -} diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java deleted file mode 100644 index 27eaec84450..00000000000 --- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.DeleteCommand; - -/** - * As we are only doing white-box testing, our test cases do not cover path variations - * outside of the DeleteCommand code. For example, inputs "1" and "1 abc" take the - * same path through the DeleteCommand, and therefore we test only one of them. - * The path variation for those two cases occur inside the ParserUtil, and - * therefore should be covered by the ParserUtilTest. - */ -public class DeleteCommandParserTest { - - private DeleteCommandParser parser = new DeleteCommandParser(); - - @Test - public void parse_validArgs_returnsDeleteCommand() { - assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON)); - } - - @Test - public void parse_invalidArgs_throwsParseException() { - assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); - } -} diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java deleted file mode 100644 index fec76fb7129..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class containing a list of {@code Person} objects to be used in tests. - */ -public class TypicalPersons { - - public static final Person ALICE = new PersonBuilder().withName("Alice Pauline") - .withAddress("123, Jurong West Ave 6, #08-111").withEmail("alice@example.com") - .withPhone("94351253") - .withTags("friends").build(); - public static final Person BENSON = new PersonBuilder().withName("Benson Meier") - .withAddress("311, Clementi Ave 2, #02-25") - .withEmail("johnd@example.com").withPhone("98765432") - .withTags("owesMoney", "friends").build(); - public static final Person CARL = new PersonBuilder().withName("Carl Kurz").withPhone("95352563") - .withEmail("heinz@example.com").withAddress("wall street").build(); - public static final Person DANIEL = new PersonBuilder().withName("Daniel Meier").withPhone("87652533") - .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends").build(); - public static final Person ELLE = new PersonBuilder().withName("Elle Meyer").withPhone("9482224") - .withEmail("werner@example.com").withAddress("michegan ave").build(); - public static final Person FIONA = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427") - .withEmail("lydia@example.com").withAddress("little tokyo").build(); - public static final Person GEORGE = new PersonBuilder().withName("George Best").withPhone("9482442") - .withEmail("anna@example.com").withAddress("4th street").build(); - - // Manually added - public static final Person HOON = new PersonBuilder().withName("Hoon Meier").withPhone("8482424") - .withEmail("stefan@example.com").withAddress("little india").build(); - public static final Person IDA = new PersonBuilder().withName("Ida Mueller").withPhone("8482131") - .withEmail("hans@example.com").withAddress("chicago ave").build(); - - // Manually added - Person's details found in {@code CommandTestUtil} - public static final Person AMY = new PersonBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY) - .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY).withTags(VALID_TAG_FRIEND).build(); - public static final Person BOB = new PersonBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND) - .build(); - - public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER - - private TypicalPersons() {} // prevents instantiation - - /** - * Returns an {@code AddressBook} with all the typical persons. - */ - public static AddressBook getTypicalAddressBook() { - AddressBook ab = new AddressBook(); - for (Person person : getTypicalPersons()) { - ab.addPerson(person); - } - return ab; - } - - public static List getTypicalPersons() { - return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); - } -} diff --git a/src/test/java/seedu/address/AppParametersTest.java b/src/test/java/vimification/AppParametersTest.java similarity index 98% rename from src/test/java/seedu/address/AppParametersTest.java rename to src/test/java/vimification/AppParametersTest.java index 61326b2d31a..dd05bd9a2c1 100644 --- a/src/test/java/seedu/address/AppParametersTest.java +++ b/src/test/java/vimification/AppParametersTest.java @@ -1,4 +1,4 @@ -package seedu.address; +package vimification; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/seedu/address/commons/core/ConfigTest.java b/src/test/java/vimification/commons/core/ConfigTest.java similarity index 95% rename from src/test/java/seedu/address/commons/core/ConfigTest.java rename to src/test/java/vimification/commons/core/ConfigTest.java index 07cd7f73d53..ca88f4b30f5 100644 --- a/src/test/java/seedu/address/commons/core/ConfigTest.java +++ b/src/test/java/vimification/commons/core/ConfigTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package vimification.commons.core; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/seedu/address/commons/core/VersionTest.java b/src/test/java/vimification/commons/core/VersionTest.java similarity index 91% rename from src/test/java/seedu/address/commons/core/VersionTest.java rename to src/test/java/vimification/commons/core/VersionTest.java index 495cd231554..982033722d4 100644 --- a/src/test/java/seedu/address/commons/core/VersionTest.java +++ b/src/test/java/vimification/commons/core/VersionTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.core; +package vimification.commons.core; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -17,7 +17,8 @@ public void versionParsing_acceptableVersionString_parsedVersionCorrectly() { @Test public void versionParsing_wrongVersionString_throwIllegalArgumentException() { - assertThrows(IllegalArgumentException.class, () -> Version.fromString("This is not a version string")); + assertThrows(IllegalArgumentException.class, + () -> Version.fromString("This is not a version string")); } @Test @@ -94,12 +95,14 @@ public void versionComparable_validVersion_compareToIsCorrect() { another = new Version(2, 15, 0, false); assertTrue(one.compareTo(another) < 0); - // Tests early access lower version vs not early access higher version compare by version number first + // Tests early access lower version vs not early access higher version compare by version + // number first one = new Version(2, 15, 0, true); another = new Version(2, 15, 5, false); assertTrue(one.compareTo(another) < 0); - // Tests early access higher version vs not early access lower version compare by version number first + // Tests early access higher version vs not early access lower version compare by version + // number first one = new Version(2, 15, 0, false); another = new Version(2, 15, 5, true); assertTrue(one.compareTo(another) < 0); @@ -130,6 +133,7 @@ public void versionComparable_validVersion_equalIsCorrect() { private void verifyVersionParsedCorrectly(String versionString, int major, int minor, int patch, boolean isEarlyAccess) { - assertEquals(new Version(major, minor, patch, isEarlyAccess), Version.fromString(versionString)); + assertEquals(new Version(major, minor, patch, isEarlyAccess), + Version.fromString(versionString)); } } diff --git a/src/test/java/seedu/address/commons/core/index/IndexTest.java b/src/test/java/vimification/commons/core/index/IndexTest.java similarity index 95% rename from src/test/java/seedu/address/commons/core/index/IndexTest.java rename to src/test/java/vimification/commons/core/index/IndexTest.java index a3ec6f8e747..887aedc27f5 100644 --- a/src/test/java/seedu/address/commons/core/index/IndexTest.java +++ b/src/test/java/vimification/commons/core/index/IndexTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.core.index; +package vimification.commons.core.index; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/vimification/commons/util/AppUtilTest.java similarity index 85% rename from src/test/java/seedu/address/commons/util/AppUtilTest.java rename to src/test/java/vimification/commons/util/AppUtilTest.java index 594de1e6365..9cb392f2b14 100644 --- a/src/test/java/seedu/address/commons/util/AppUtilTest.java +++ b/src/test/java/vimification/commons/util/AppUtilTest.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package vimification.commons.util; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -31,6 +31,7 @@ public void checkArgument_falseWithoutErrorMessage_throwsIllegalArgumentExceptio @Test public void checkArgument_falseWithErrorMessage_throwsIllegalArgumentException() { String errorMessage = "error message"; - assertThrows(IllegalArgumentException.class, errorMessage, () -> AppUtil.checkArgument(false, errorMessage)); + assertThrows(IllegalArgumentException.class, errorMessage, + () -> AppUtil.checkArgument(false, errorMessage)); } } diff --git a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java b/src/test/java/vimification/commons/util/CollectionUtilTest.java similarity index 90% rename from src/test/java/seedu/address/commons/util/CollectionUtilTest.java rename to src/test/java/vimification/commons/util/CollectionUtilTest.java index b467a3dc025..1ee7c96d081 100644 --- a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java +++ b/src/test/java/vimification/commons/util/CollectionUtilTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package vimification.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.commons.util.CollectionUtil.requireAllNonNull; +import static vimification.testutil.Assert.assertThrows; import java.util.Arrays; import java.util.Collection; @@ -83,16 +83,17 @@ public void isAnyNonNull() { } /** - * Asserts that {@code CollectionUtil#requireAllNonNull(Object...)} throw {@code NullPointerException} - * if {@code objects} or any element of {@code objects} is null. + * Asserts that {@code CollectionUtil#requireAllNonNull(Object...)} throw + * {@code NullPointerException} if {@code objects} or any element of {@code objects} is null. */ private void assertNullPointerExceptionThrown(Object... objects) { assertThrows(NullPointerException.class, () -> requireAllNonNull(objects)); } /** - * Asserts that {@code CollectionUtil#requireAllNonNull(Collection)} throw {@code NullPointerException} - * if {@code collection} or any element of {@code collection} is null. + * Asserts that {@code CollectionUtil#requireAllNonNull(Collection)} throw + * {@code NullPointerException} if {@code collection} or any element of {@code collection} is + * null. */ private void assertNullPointerExceptionThrown(Collection collection) { assertThrows(NullPointerException.class, () -> requireAllNonNull(collection)); diff --git a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java b/src/test/java/vimification/commons/util/ConfigUtilTest.java similarity index 88% rename from src/test/java/seedu/address/commons/util/ConfigUtilTest.java rename to src/test/java/vimification/commons/util/ConfigUtilTest.java index d2ab2839a52..827f9c52f30 100644 --- a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java +++ b/src/test/java/vimification/commons/util/ConfigUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package vimification.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -13,8 +13,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import vimification.commons.core.Config; +import vimification.commons.exceptions.DataConversionException; public class ConfigUtilTest { @@ -68,7 +68,8 @@ private Config getTypicalConfig() { return config; } - private Optional read(String configFileInTestDataFolder) throws DataConversionException { + private Optional read(String configFileInTestDataFolder) + throws DataConversionException { Path configFilePath = addToTestDataPathIfNotNull(configFileInTestDataFolder); return ConfigUtil.readConfig(configFilePath); } @@ -89,12 +90,12 @@ public void saveConfig_allInOrder_success() throws DataConversionException, IOEx Path configFilePath = tempDir.resolve("TempConfig.json"); - //Try writing when the file doesn't exist + // Try writing when the file doesn't exist ConfigUtil.saveConfig(original, configFilePath); Config readBack = ConfigUtil.readConfig(configFilePath).get(); assertEquals(original, readBack); - //Try saving when the file exists + // Try saving when the file exists original.setLogLevel(Level.FINE); ConfigUtil.saveConfig(original, configFilePath); readBack = ConfigUtil.readConfig(configFilePath).get(); @@ -108,8 +109,8 @@ private void save(Config config, String configFileInTestDataFolder) throws IOExc private Path addToTestDataPathIfNotNull(String configFileInTestDataFolder) { return configFileInTestDataFolder != null - ? TEST_DATA_FOLDER.resolve(configFileInTestDataFolder) - : null; + ? TEST_DATA_FOLDER.resolve(configFileInTestDataFolder) + : null; } diff --git a/src/test/java/seedu/address/commons/util/FileUtilTest.java b/src/test/java/vimification/commons/util/FileUtilTest.java similarity index 84% rename from src/test/java/seedu/address/commons/util/FileUtilTest.java rename to src/test/java/vimification/commons/util/FileUtilTest.java index 1fe5478c756..832f875047e 100644 --- a/src/test/java/seedu/address/commons/util/FileUtilTest.java +++ b/src/test/java/vimification/commons/util/FileUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package vimification.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/util/JsonUtilTest.java b/src/test/java/vimification/commons/util/JsonUtilTest.java similarity index 63% rename from src/test/java/seedu/address/commons/util/JsonUtilTest.java rename to src/test/java/vimification/commons/util/JsonUtilTest.java index d4907539dee..99271defa4c 100644 --- a/src/test/java/seedu/address/commons/util/JsonUtilTest.java +++ b/src/test/java/vimification/commons/util/JsonUtilTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package vimification.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -7,15 +7,16 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.SerializableTestClass; -import seedu.address.testutil.TestUtil; +import vimification.testutil.SerializableTestClass; +import vimification.testutil.TestUtil; /** * Tests JSON Read and Write */ public class JsonUtilTest { - private static final Path SERIALIZATION_FILE = TestUtil.getFilePathInSandboxFolder("serialize.json"); + private static final Path SERIALIZATION_FILE = + TestUtil.getFilePathInSandboxFolder("serialize.json"); @Test public void serializeObjectToJsonFile_noExceptionThrown() throws IOException { @@ -24,7 +25,8 @@ public void serializeObjectToJsonFile_noExceptionThrown() throws IOException { JsonUtil.serializeObjectToJsonFile(SERIALIZATION_FILE, serializableTestClass); - assertEquals(FileUtil.readFromFile(SERIALIZATION_FILE), SerializableTestClass.JSON_STRING_REPRESENTATION); + assertEquals(FileUtil.readFromFile(SERIALIZATION_FILE), + SerializableTestClass.JSON_STRING_REPRESENTATION); } @Test @@ -35,11 +37,13 @@ public void deserializeObjectFromJsonFile_noExceptionThrown() throws IOException .deserializeObjectFromJsonFile(SERIALIZATION_FILE, SerializableTestClass.class); assertEquals(serializableTestClass.getName(), SerializableTestClass.getNameTestValue()); - assertEquals(serializableTestClass.getListOfLocalDateTimes(), SerializableTestClass.getListTestValues()); - assertEquals(serializableTestClass.getMapOfIntegerToString(), SerializableTestClass.getHashMapTestValues()); + assertEquals(serializableTestClass.getListOfLocalDateTimes(), + SerializableTestClass.getListTestValues()); + assertEquals(serializableTestClass.getMapOfIntegerToString(), + SerializableTestClass.getHashMapTestValues()); } - //TODO: @Test jsonUtil_readJsonStringToObjectInstance_correctObject() + // TODO: @Test jsonUtil_readJsonStringToObjectInstance_correctObject() - //TODO: @Test jsonUtil_writeThenReadObjectToJson_correctObject() + // TODO: @Test jsonUtil_writeThenReadObjectToJson_correctObject() } diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/vimification/commons/util/StringUtilTest.java similarity index 58% rename from src/test/java/seedu/address/commons/util/StringUtilTest.java rename to src/test/java/vimification/commons/util/StringUtilTest.java index c56d407bf3f..bac805267ed 100644 --- a/src/test/java/seedu/address/commons/util/StringUtilTest.java +++ b/src/test/java/vimification/commons/util/StringUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package vimification.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import java.io.FileNotFoundException; @@ -10,7 +10,7 @@ public class StringUtilTest { - //---------------- Tests for isNonZeroUnsignedInteger -------------------------------------- + // ---------------- Tests for isNonZeroUnsignedInteger -------------------------------------- @Test public void isNonZeroUnsignedInteger() { @@ -46,57 +46,49 @@ public void isNonZeroUnsignedInteger() { } - //---------------- Tests for containsWordIgnoreCase -------------------------------------- + // ---------------- Tests for containsWordIgnoreCase -------------------------------------- /* - * Invalid equivalence partitions for word: null, empty, multiple words - * Invalid equivalence partitions for sentence: null - * The four test cases below test one invalid input at a time. + * Invalid equivalence partitions for word: null, empty, multiple words Invalid equivalence + * partitions for sentence: null The four test cases below test one invalid input at a time. */ @Test public void containsWordIgnoreCase_nullWord_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> StringUtil.containsWordIgnoreCase("typical sentence", null)); + assertThrows(NullPointerException.class, + () -> StringUtil.containsWordIgnoreCase("typical sentence", null)); } @Test public void containsWordIgnoreCase_emptyWord_throwsIllegalArgumentException() { - assertThrows(IllegalArgumentException.class, "Word parameter cannot be empty", () - -> StringUtil.containsWordIgnoreCase("typical sentence", " ")); + assertThrows(IllegalArgumentException.class, "Word parameter cannot be empty", + () -> StringUtil.containsWordIgnoreCase("typical sentence", " ")); } @Test public void containsWordIgnoreCase_multipleWords_throwsIllegalArgumentException() { - assertThrows(IllegalArgumentException.class, "Word parameter should be a single word", () - -> StringUtil.containsWordIgnoreCase("typical sentence", "aaa BBB")); + assertThrows(IllegalArgumentException.class, "Word parameter should be a single word", + () -> StringUtil.containsWordIgnoreCase("typical sentence", "aaa BBB")); } @Test public void containsWordIgnoreCase_nullSentence_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> StringUtil.containsWordIgnoreCase(null, "abc")); + assertThrows(NullPointerException.class, + () -> StringUtil.containsWordIgnoreCase(null, "abc")); } /* - * Valid equivalence partitions for word: - * - any word - * - word containing symbols/numbers - * - word with leading/trailing spaces + * Valid equivalence partitions for word: - any word - word containing symbols/numbers - word + * with leading/trailing spaces * - * Valid equivalence partitions for sentence: - * - empty string - * - one word - * - multiple words - * - sentence with extra spaces + * Valid equivalence partitions for sentence: - empty string - one word - multiple words - + * sentence with extra spaces * - * Possible scenarios returning true: - * - matches first word in sentence - * - last word in sentence - * - middle word in sentence - * - matches multiple words + * Possible scenarios returning true: - matches first word in sentence - last word in sentence - + * middle word in sentence - matches multiple words * - * Possible scenarios returning false: - * - query word matches part of a sentence word - * - sentence word matches part of the query word + * Possible scenarios returning false: - query word matches part of a sentence word - sentence + * word matches part of the query word * * The test method below tries to verify all above with a reasonably low number of test cases. */ @@ -109,21 +101,28 @@ public void containsWordIgnoreCase_validInputs_correctResult() { assertFalse(StringUtil.containsWordIgnoreCase(" ", "123")); // Matches a partial word only - assertFalse(StringUtil.containsWordIgnoreCase("aaa bbb ccc", "bb")); // Sentence word bigger than query word - assertFalse(StringUtil.containsWordIgnoreCase("aaa bbb ccc", "bbbb")); // Query word bigger than sentence word + assertFalse(StringUtil.containsWordIgnoreCase("aaa bbb ccc", "bb")); // Sentence word bigger + // than query word + assertFalse(StringUtil.containsWordIgnoreCase("aaa bbb ccc", "bbbb")); // Query word bigger + // than sentence word // Matches word in the sentence, different upper/lower case letters - assertTrue(StringUtil.containsWordIgnoreCase("aaa bBb ccc", "Bbb")); // First word (boundary case) - assertTrue(StringUtil.containsWordIgnoreCase("aaa bBb ccc@1", "CCc@1")); // Last word (boundary case) - assertTrue(StringUtil.containsWordIgnoreCase(" AAA bBb ccc ", "aaa")); // Sentence has extra spaces - assertTrue(StringUtil.containsWordIgnoreCase("Aaa", "aaa")); // Only one word in sentence (boundary case) - assertTrue(StringUtil.containsWordIgnoreCase("aaa bbb ccc", " ccc ")); // Leading/trailing spaces + assertTrue(StringUtil.containsWordIgnoreCase("aaa bBb ccc", "Bbb")); // First word (boundary + // case) + assertTrue(StringUtil.containsWordIgnoreCase("aaa bBb ccc@1", "CCc@1")); // Last word + // (boundary case) + assertTrue(StringUtil.containsWordIgnoreCase(" AAA bBb ccc ", "aaa")); // Sentence has + // extra spaces + assertTrue(StringUtil.containsWordIgnoreCase("Aaa", "aaa")); // Only one word in sentence + // (boundary case) + assertTrue(StringUtil.containsWordIgnoreCase("aaa bbb ccc", " ccc ")); // Leading/trailing + // spaces // Matches multiple words in sentence assertTrue(StringUtil.containsWordIgnoreCase("AAA bBb ccc bbb", "bbB")); } - //---------------- Tests for getDetails -------------------------------------- + // ---------------- Tests for getDetails -------------------------------------- /* * Equivalence Partitions: null, valid throwable object @@ -132,7 +131,7 @@ public void containsWordIgnoreCase_validInputs_correctResult() { @Test public void getDetails_exceptionGiven() { assertTrue(StringUtil.getDetails(new FileNotFoundException("file not found")) - .contains("java.io.FileNotFoundException: file not found")); + .contains("java.io.FileNotFoundException: file not found")); } @Test diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/vimification/logic/LogicManagerTest.java similarity index 66% rename from src/test/java/seedu/address/logic/LogicManagerTest.java rename to src/test/java/vimification/logic/LogicManagerTest.java index ad923ac249a..2ebc7946a5c 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/vimification/logic/LogicManagerTest.java @@ -1,14 +1,14 @@ -package seedu.address.logic; +package vimification.logic; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.AMY; +import static vimification.commons.core.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; +import static vimification.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static vimification.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.NAME_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +import static vimification.testutil.Assert.assertThrows; +import static vimification.testutil.TypicalPersons.AMY; import java.io.IOException; import java.nio.file.Path; @@ -17,20 +17,20 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.StorageManager; -import seedu.address.testutil.PersonBuilder; +import vimification.logic.commands.AddCommand; +import vimification.logic.commands.CommandResult; +import vimification.logic.commands.ListCommand; +import vimification.logic.commands.exceptions.CommandException; +import vimification.logic.parser.exceptions.ParseException; +import vimification.model.Model; +import vimification.model.ModelManager; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.UserPrefs; +import vimification.model.person.Person; +import vimification.storage.JsonAddressBookStorage; +import vimification.storage.JsonUserPrefsStorage; +import vimification.storage.StorageManager; +import vimification.testutil.PersonBuilder; public class LogicManagerTest { private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy exception"); @@ -45,7 +45,8 @@ public class LogicManagerTest { public void setUp() { JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json")); - JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); + JsonUserPrefsStorage userPrefsStorage = + new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); logic = new LogicManager(model, storage); } @@ -72,15 +73,17 @@ public void execute_validCommand_success() throws Exception { public void execute_storageThrowsIoException_throwsCommandException() { // Setup LogicManager with JsonAddressBookIoExceptionThrowingStub JsonAddressBookStorage addressBookStorage = - new JsonAddressBookIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionAddressBook.json")); + new JsonAddressBookIoExceptionThrowingStub( + temporaryFolder.resolve("ioExceptionAddressBook.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("ioExceptionUserPrefs.json")); StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); logic = new LogicManager(model, storage); // Execute add command - String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY - + ADDRESS_DESC_AMY; + String addCommand = + AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + + ADDRESS_DESC_AMY; Person expectedPerson = new PersonBuilder(AMY).withTags().build(); ModelManager expectedModel = new ModelManager(); expectedModel.addPerson(expectedPerson); @@ -90,14 +93,15 @@ public void execute_storageThrowsIoException_throwsCommandException() { @Test public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0)); + assertThrows(UnsupportedOperationException.class, + () -> logic.getFilteredPersonList().remove(0)); } /** - * Executes the command and confirms that - * - no exceptions are thrown
+ * Executes the command and confirms that - no exceptions are thrown
* - the feedback message is equal to {@code expectedMessage}
* - the internal model manager state is the same as that in {@code expectedModel}
+ * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertCommandSuccess(String inputCommand, String expectedMessage, @@ -108,7 +112,9 @@ private void assertCommandSuccess(String inputCommand, String expectedMessage, } /** - * Executes the command, confirms that a ParseException is thrown and that the result message is correct. + * Executes the command, confirms that a ParseException is thrown and that the result message is + * correct. + * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertParseException(String inputCommand, String expectedMessage) { @@ -116,7 +122,9 @@ private void assertParseException(String inputCommand, String expectedMessage) { } /** - * Executes the command, confirms that a CommandException is thrown and that the result message is correct. + * Executes the command, confirms that a CommandException is thrown and that the result message + * is correct. + * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertCommandException(String inputCommand, String expectedMessage) { @@ -124,23 +132,27 @@ private void assertCommandException(String inputCommand, String expectedMessage) } /** - * Executes the command, confirms that the exception is thrown and that the result message is correct. + * Executes the command, confirms that the exception is thrown and that the result message is + * correct. + * * @see #assertCommandFailure(String, Class, String, Model) */ - private void assertCommandFailure(String inputCommand, Class expectedException, + private void assertCommandFailure(String inputCommand, + Class expectedException, String expectedMessage) { Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel); } /** - * Executes the command and confirms that - * - the {@code expectedException} is thrown
+ * Executes the command and confirms that - the {@code expectedException} is thrown
* - the resulting error message is equal to {@code expectedMessage}
* - the internal model manager state is the same as that in {@code expectedModel}
+ * * @see #assertCommandSuccess(String, String, Model) */ - private void assertCommandFailure(String inputCommand, Class expectedException, + private void assertCommandFailure(String inputCommand, + Class expectedException, String expectedMessage, Model expectedModel) { assertThrows(expectedException, expectedMessage, () -> logic.execute(inputCommand)); assertEquals(expectedModel, model); @@ -155,7 +167,8 @@ private JsonAddressBookIoExceptionThrowingStub(Path filePath) { } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { + public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) + throws IOException { throw DUMMY_IO_EXCEPTION; } } diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/vimification/logic/commands/AddCommandIntegrationTest.java similarity index 65% rename from src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java rename to src/test/java/vimification/logic/commands/AddCommandIntegrationTest.java index cb8714bb055..60ac73a9439 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java +++ b/src/test/java/vimification/logic/commands/AddCommandIntegrationTest.java @@ -1,17 +1,17 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static vimification.logic.commands.CommandTestUtil.assertCommandFailure; +import static vimification.logic.commands.CommandTestUtil.assertCommandSuccess; +import static vimification.testutil.TypicalPersons.getTypicalAddressBook; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -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; +import vimification.model.Model; +import vimification.model.ModelManager; +import vimification.model.UserPrefs; +import vimification.model.person.Person; +import vimification.testutil.PersonBuilder; /** * Contains integration tests (interaction with the Model) for {@code AddCommand}. @@ -39,7 +39,8 @@ public void execute_newPerson_success() { @Test public void execute_duplicatePerson_throwsCommandException() { Person personInList = model.getAddressBook().getPersonList().get(0); - assertCommandFailure(new AddCommand(personInList), model, AddCommand.MESSAGE_DUPLICATE_PERSON); + assertCommandFailure(new AddCommand(personInList), model, + AddCommand.MESSAGE_DUPLICATE_PERSON); } } diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/vimification/logic/commands/AddCommandTest.java similarity index 90% rename from src/test/java/seedu/address/logic/commands/AddCommandTest.java rename to src/test/java/vimification/logic/commands/AddCommandTest.java index 5865713d5dd..9fab0ff53f4 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ b/src/test/java/vimification/logic/commands/AddCommandTest.java @@ -1,10 +1,10 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static java.util.Objects.requireNonNull; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import java.nio.file.Path; import java.util.ArrayList; @@ -14,14 +14,14 @@ import org.junit.jupiter.api.Test; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; +import vimification.commons.core.GuiSettings; +import vimification.logic.commands.exceptions.CommandException; +import vimification.model.AddressBook; +import vimification.model.Model; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyUserPrefs; +import vimification.model.person.Person; +import vimification.testutil.PersonBuilder; public class AddCommandTest { @@ -37,7 +37,8 @@ public void execute_personAcceptedByModel_addSuccessful() throws Exception { CommandResult commandResult = new AddCommand(validPerson).execute(modelStub); - assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validPerson), commandResult.getFeedbackToUser()); + assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validPerson), + commandResult.getFeedbackToUser()); assertEquals(Arrays.asList(validPerson), modelStub.personsAdded); } @@ -47,7 +48,8 @@ public void execute_duplicatePerson_throwsCommandException() { AddCommand addCommand = new AddCommand(validPerson); ModelStub modelStub = new ModelStubWithPerson(validPerson); - assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub)); + assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, + () -> addCommand.execute(modelStub)); } @Test diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/vimification/logic/commands/ClearCommandTest.java similarity index 59% rename from src/test/java/seedu/address/logic/commands/ClearCommandTest.java rename to src/test/java/vimification/logic/commands/ClearCommandTest.java index 80d9110c03a..ddb344d9af7 100644 --- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java +++ b/src/test/java/vimification/logic/commands/ClearCommandTest.java @@ -1,14 +1,14 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static vimification.logic.commands.CommandTestUtil.assertCommandSuccess; +import static vimification.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 vimification.model.AddressBook; +import vimification.model.Model; +import vimification.model.ModelManager; +import vimification.model.UserPrefs; public class ClearCommandTest { @@ -17,7 +17,8 @@ public void execute_emptyAddressBook_success() { Model model = new ModelManager(); Model expectedModel = new ModelManager(); - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, + expectedModel); } @Test @@ -26,7 +27,8 @@ public void execute_nonEmptyAddressBook_success() { Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); expectedModel.setAddressBook(new AddressBook()); - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, + expectedModel); } } diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/vimification/logic/commands/CommandResultTest.java similarity index 86% rename from src/test/java/seedu/address/logic/commands/CommandResultTest.java rename to src/test/java/vimification/logic/commands/CommandResultTest.java index 4f3eb46e9ef..5ef16b2593e 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/vimification/logic/commands/CommandResultTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -46,9 +46,11 @@ public void hashcode() { assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode()); // different showHelp value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode()); + assertNotEquals(commandResult.hashCode(), + new CommandResult("feedback", true, false).hashCode()); // different exit value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode()); + assertNotEquals(commandResult.hashCode(), + new CommandResult("feedback", false, true).hashCode()); } } diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/vimification/logic/commands/CommandTestUtil.java similarity index 73% rename from src/test/java/seedu/address/logic/commands/CommandTestUtil.java rename to src/test/java/vimification/logic/commands/CommandTestUtil.java index 643a1d08069..934ea92c67f 100644 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ b/src/test/java/vimification/logic/commands/CommandTestUtil.java @@ -1,25 +1,25 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -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.testutil.Assert.assertThrows; +import static vimification.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static vimification.logic.parser.CliSyntax.PREFIX_EMAIL; +import static vimification.logic.parser.CliSyntax.PREFIX_NAME; +import static vimification.logic.parser.CliSyntax.PREFIX_PHONE; +import static vimification.logic.parser.CliSyntax.PREFIX_TAG; +import static vimification.testutil.Assert.assertThrows; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; +import vimification.commons.core.index.Index; +import vimification.logic.commands.exceptions.CommandException; +import vimification.model.AddressBook; +import vimification.model.Model; +import vimification.model.person.NameContainsKeywordsPredicate; +import vimification.model.person.Person; +import vimification.testutil.EditPersonDescriptorBuilder; /** * Contains helper methods for testing commands. @@ -48,11 +48,16 @@ public class CommandTestUtil { public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND; public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND; - public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names - public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones - public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' symbol - public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for addresses - public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags + public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed + // in names + public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed + // in phones + public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' + // symbol + public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not + // allowed for addresses + public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in + // tags public static final String PREAMBLE_WHITESPACE = "\t \r \n"; public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; @@ -62,10 +67,12 @@ public class CommandTestUtil { static { DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) + .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY) + .withAddress(VALID_ADDRESS_AMY) .withTags(VALID_TAG_FRIEND).build(); DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB) + .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) + .withAddress(VALID_ADDRESS_BOB) .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); } @@ -74,7 +81,8 @@ public class CommandTestUtil { * - the returned {@link CommandResult} matches {@code expectedCommandResult}
* - the {@code actualModel} matches {@code expectedModel} */ - public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult, + public static void assertCommandSuccess(Command command, Model actualModel, + CommandResult expectedCommandResult, Model expectedModel) { try { CommandResult result = command.execute(actualModel); @@ -89,7 +97,8 @@ public static void assertCommandSuccess(Command command, Model actualModel, Comm * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)} * that takes a string {@code expectedMessage}. */ - public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, + public static void assertCommandSuccess(Command command, Model actualModel, + String expectedMessage, Model expectedModel) { CommandResult expectedCommandResult = new CommandResult(expectedMessage); assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); @@ -99,9 +108,11 @@ public static void assertCommandSuccess(Command command, Model actualModel, Stri * Executes the given {@code command}, confirms that
* - a {@code CommandException} is thrown
* - the CommandException message matches {@code expectedMessage}
- * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged + * - the address book, filtered person list and selected person in {@code actualModel} remain + * unchanged */ - public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { + public static void assertCommandFailure(Command command, Model actualModel, + String expectedMessage) { // we are unable to defensively copy the model for comparison later, so we can // only do so by copying its components. AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook()); @@ -111,16 +122,18 @@ public static void assertCommandFailure(Command command, Model actualModel, Stri assertEquals(expectedAddressBook, actualModel.getAddressBook()); assertEquals(expectedFilteredList, actualModel.getFilteredPersonList()); } + /** - * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the - * {@code model}'s address book. + * Updates {@code model}'s filtered list to show only the person at the given + * {@code targetIndex} in the {@code model}'s address book. */ public static void showPersonAtIndex(Model model, Index targetIndex) { assertTrue(targetIndex.getZeroBased() < model.getFilteredPersonList().size()); Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased()); final String[] splitName = person.getName().fullName.split("\\s+"); - model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); + model.updateFilteredPersonList( + new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); assertEquals(1, model.getFilteredPersonList().size()); } diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/vimification/logic/commands/DeleteCommandTest.java similarity index 71% rename from src/test/java/seedu/address/logic/commands/DeleteCommandTest.java rename to src/test/java/vimification/logic/commands/DeleteCommandTest.java index 45a8c910ba1..e7be276818e 100644 --- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java +++ b/src/test/java/vimification/logic/commands/DeleteCommandTest.java @@ -1,26 +1,25 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static vimification.logic.commands.CommandTestUtil.assertCommandFailure; +import static vimification.logic.commands.CommandTestUtil.assertCommandSuccess; +import static vimification.logic.commands.CommandTestUtil.showPersonAtIndex; +import static vimification.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static vimification.testutil.TypicalIndexes.INDEX_SECOND_PERSON; +import static vimification.testutil.TypicalPersons.getTypicalAddressBook; import org.junit.jupiter.api.Test; -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; +import vimification.commons.core.Messages; +import vimification.commons.core.index.Index; +import vimification.model.Model; +import vimification.model.ModelManager; +import vimification.model.UserPrefs; +import vimification.model.person.Person; /** - * Contains integration tests (interaction with the Model) and unit tests for - * {@code DeleteCommand}. + * Contains integration tests (interaction with the Model) and unit tests for {@code DeleteCommand}. */ public class DeleteCommandTest { @@ -28,10 +27,12 @@ public class DeleteCommandTest { @Test public void execute_validIndexUnfilteredList_success() { - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + Person personToDelete = + model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); + String expectedMessage = + String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); expectedModel.deletePerson(personToDelete); @@ -51,10 +52,12 @@ public void execute_invalidIndexUnfilteredList_throwsCommandException() { public void execute_validIndexFilteredList_success() { showPersonAtIndex(model, INDEX_FIRST_PERSON); - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + Person personToDelete = + model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); + String expectedMessage = + String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); expectedModel.deletePerson(personToDelete); diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/vimification/logic/commands/EditCommandTest.java similarity index 66% rename from src/test/java/seedu/address/logic/commands/EditCommandTest.java rename to src/test/java/vimification/logic/commands/EditCommandTest.java index 214c6c2507b..29a4109f8aa 100644 --- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java +++ b/src/test/java/vimification/logic/commands/EditCommandTest.java @@ -1,31 +1,31 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static vimification.logic.commands.CommandTestUtil.DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.DESC_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static vimification.logic.commands.CommandTestUtil.assertCommandFailure; +import static vimification.logic.commands.CommandTestUtil.assertCommandSuccess; +import static vimification.logic.commands.CommandTestUtil.showPersonAtIndex; +import static vimification.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static vimification.testutil.TypicalIndexes.INDEX_SECOND_PERSON; +import static vimification.testutil.TypicalPersons.getTypicalAddressBook; import org.junit.jupiter.api.Test; -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -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.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; +import vimification.commons.core.Messages; +import vimification.commons.core.index.Index; +import vimification.logic.commands.EditCommand.EditPersonDescriptor; +import vimification.model.AddressBook; +import vimification.model.Model; +import vimification.model.ModelManager; +import vimification.model.UserPrefs; +import vimification.model.person.Person; +import vimification.testutil.EditPersonDescriptorBuilder; +import vimification.testutil.PersonBuilder; /** * Contains integration tests (interaction with the Model) and unit tests for EditCommand. @@ -40,9 +40,11 @@ public void execute_allFieldsSpecifiedUnfilteredList_success() { EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build(); EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = + String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = + new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); @@ -61,9 +63,11 @@ public void execute_someFieldsSpecifiedUnfilteredList_success() { .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build(); EditCommand editCommand = new EditCommand(indexLastPerson, descriptor); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = + String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = + new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); expectedModel.setPerson(lastPerson, editedPerson); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); @@ -74,9 +78,11 @@ public void execute_noFieldSpecifiedUnfilteredList_success() { EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor()); Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = + String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = + new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } @@ -85,14 +91,18 @@ public void execute_noFieldSpecifiedUnfilteredList_success() { public void execute_filteredList_success() { showPersonAtIndex(model, INDEX_FIRST_PERSON); - Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build(); + Person personInFilteredList = + model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + Person editedPerson = + new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build(); EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = + String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = + new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); @@ -112,7 +122,8 @@ public void execute_duplicatePersonFilteredList_failure() { showPersonAtIndex(model, INDEX_FIRST_PERSON); // edit person in filtered list into a duplicate in address book - Person personInList = model.getAddressBook().getPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); + Person personInList = + model.getAddressBook().getPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptorBuilder(personInList).build()); @@ -122,15 +133,16 @@ public void execute_duplicatePersonFilteredList_failure() { @Test public void execute_invalidPersonIndexUnfilteredList_failure() { Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build(); + EditPersonDescriptor descriptor = + new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build(); EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor); assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } /** - * Edit filtered list where index is larger than size of filtered list, - * but smaller than size of address book + * Edit filtered list where index is larger than size of filtered list, but smaller than size of + * address book */ @Test public void execute_invalidPersonIndexFilteredList_failure() { diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/vimification/logic/commands/EditPersonDescriptorTest.java similarity index 62% rename from src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java rename to src/test/java/vimification/logic/commands/EditPersonDescriptorTest.java index e0288792e72..eb8fa0cf737 100644 --- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java +++ b/src/test/java/vimification/logic/commands/EditPersonDescriptorTest.java @@ -1,19 +1,19 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static vimification.logic.commands.CommandTestUtil.DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.DESC_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.testutil.EditPersonDescriptorBuilder; +import vimification.logic.commands.EditCommand.EditPersonDescriptor; +import vimification.testutil.EditPersonDescriptorBuilder; public class EditPersonDescriptorTest { @@ -36,7 +36,8 @@ public void equals() { assertFalse(DESC_AMY.equals(DESC_BOB)); // different name -> returns false - EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build(); + EditPersonDescriptor editedAmy = + new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build(); assertFalse(DESC_AMY.equals(editedAmy)); // different phone -> returns false @@ -48,7 +49,8 @@ public void equals() { assertFalse(DESC_AMY.equals(editedAmy)); // different address -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build(); + editedAmy = + new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build(); assertFalse(DESC_AMY.equals(editedAmy)); // different tags -> returns false diff --git a/src/test/java/vimification/logic/commands/ExitCommandTest.java b/src/test/java/vimification/logic/commands/ExitCommandTest.java new file mode 100644 index 00000000000..372c22b4e9f --- /dev/null +++ b/src/test/java/vimification/logic/commands/ExitCommandTest.java @@ -0,0 +1,21 @@ +package vimification.logic.commands; + +import static vimification.logic.commands.CommandTestUtil.assertCommandSuccess; +import static vimification.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; + +import org.junit.jupiter.api.Test; + +import vimification.model.Model; +import vimification.model.ModelManager; + +public class ExitCommandTest { + private Model model = new ModelManager(); + private Model expectedModel = new ModelManager(); + + @Test + public void execute_exit_success() { + CommandResult expectedCommandResult = + new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/vimification/logic/commands/FindCommandTest.java similarity index 82% rename from src/test/java/seedu/address/logic/commands/FindCommandTest.java rename to src/test/java/vimification/logic/commands/FindCommandTest.java index 9b15db28bbb..199eaae3f38 100644 --- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java +++ b/src/test/java/vimification/logic/commands/FindCommandTest.java @@ -1,24 +1,24 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.CARL; -import static seedu.address.testutil.TypicalPersons.ELLE; -import static seedu.address.testutil.TypicalPersons.FIONA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static vimification.commons.core.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; +import static vimification.logic.commands.CommandTestUtil.assertCommandSuccess; +import static vimification.testutil.TypicalPersons.CARL; +import static vimification.testutil.TypicalPersons.ELLE; +import static vimification.testutil.TypicalPersons.FIONA; +import static vimification.testutil.TypicalPersons.getTypicalAddressBook; import java.util.Arrays; import java.util.Collections; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import vimification.model.Model; +import vimification.model.ModelManager; +import vimification.model.UserPrefs; +import vimification.model.person.NameContainsKeywordsPredicate; /** * Contains integration tests (interaction with the Model) for {@code FindCommand}. diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/vimification/logic/commands/HelpCommandTest.java similarity index 61% rename from src/test/java/seedu/address/logic/commands/HelpCommandTest.java rename to src/test/java/vimification/logic/commands/HelpCommandTest.java index 4904fc4352e..a842bacb0df 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/vimification/logic/commands/HelpCommandTest.java @@ -1,12 +1,12 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; +import static vimification.logic.commands.CommandTestUtil.assertCommandSuccess; +import static vimification.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import vimification.model.Model; +import vimification.model.ModelManager; public class HelpCommandTest { private Model model = new ModelManager(); diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/vimification/logic/commands/ListCommandTest.java similarity index 65% rename from src/test/java/seedu/address/logic/commands/ListCommandTest.java rename to src/test/java/vimification/logic/commands/ListCommandTest.java index 435ff1f7275..f5832766711 100644 --- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java +++ b/src/test/java/vimification/logic/commands/ListCommandTest.java @@ -1,16 +1,16 @@ -package seedu.address.logic.commands; +package vimification.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static vimification.logic.commands.CommandTestUtil.assertCommandSuccess; +import static vimification.logic.commands.CommandTestUtil.showPersonAtIndex; +import static vimification.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static vimification.testutil.TypicalPersons.getTypicalAddressBook; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; +import vimification.model.Model; +import vimification.model.ModelManager; +import vimification.model.UserPrefs; /** * Contains integration tests (interaction with the Model) and unit tests for ListCommand. diff --git a/src/test/java/vimification/logic/parser/AddCommandParserTest.java b/src/test/java/vimification/logic/parser/AddCommandParserTest.java new file mode 100644 index 00000000000..94c6d5f9a87 --- /dev/null +++ b/src/test/java/vimification/logic/parser/AddCommandParserTest.java @@ -0,0 +1,167 @@ +package vimification.logic.parser; + +import static vimification.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static vimification.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; +import static vimification.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; +import static vimification.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; +import static vimification.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; +import static vimification.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +import static vimification.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; +import static vimification.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static vimification.logic.commands.CommandTestUtil.NAME_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.NAME_DESC_BOB; +import static vimification.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.PHONE_DESC_BOB; +import static vimification.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; +import static vimification.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static vimification.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; +import static vimification.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; +import static vimification.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static vimification.logic.parser.CommandParserTestUtil.assertParseFailure; +import static vimification.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static vimification.testutil.TypicalPersons.AMY; +import static vimification.testutil.TypicalPersons.BOB; + +import org.junit.jupiter.api.Test; + +import vimification.logic.commands.AddCommand; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Person; +import vimification.model.person.Phone; +import vimification.model.tag.Tag; +import vimification.testutil.PersonBuilder; + +public class AddCommandParserTest { + private AddCommandParser parser = new AddCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); + + // whitespace only preamble + assertParseSuccess(parser, + PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, + new AddCommand(expectedPerson)); + + // multiple names - last name accepted + assertParseSuccess(parser, NAME_DESC_AMY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); + + // multiple phones - last phone accepted + assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_AMY + PHONE_DESC_BOB + EMAIL_DESC_BOB + + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); + + // multiple emails - last email accepted + assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB + + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); + + // multiple addresses - last address accepted + assertParseSuccess(parser, + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_AMY + + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, + new AddCommand(expectedPerson)); + + // multiple tags - all accepted + Person expectedPersonMultipleTags = + new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) + .build(); + assertParseSuccess(parser, + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, + new AddCommand(expectedPersonMultipleTags)); + } + + @Test + public void parse_optionalFieldsMissing_success() { + // zero tags + Person expectedPerson = new PersonBuilder(AMY).withTags().build(); + assertParseSuccess(parser, + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY, + new AddCommand(expectedPerson)); + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); + + // missing name prefix + assertParseFailure(parser, + VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, + expectedMessage); + + // missing phone prefix + assertParseFailure(parser, + NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, + expectedMessage); + + // missing email prefix + assertParseFailure(parser, + NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB, + expectedMessage); + + // missing address prefix + assertParseFailure(parser, + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB, + expectedMessage); + + // all prefixes missing + assertParseFailure(parser, + VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB, + expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + // invalid name + assertParseFailure(parser, + INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, + Name.MESSAGE_CONSTRAINTS); + + // invalid phone + assertParseFailure(parser, + NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, + Phone.MESSAGE_CONSTRAINTS); + + // invalid email + assertParseFailure(parser, + NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, + Email.MESSAGE_CONSTRAINTS); + + // invalid address + assertParseFailure(parser, + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, + Address.MESSAGE_CONSTRAINTS); + + // invalid tag + assertParseFailure(parser, + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB + + INVALID_TAG_DESC + VALID_TAG_FRIEND, + Tag.MESSAGE_CONSTRAINTS); + + // two invalid values, only first invalid value reported + assertParseFailure(parser, + INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC, + Name.MESSAGE_CONSTRAINTS); + + // non-empty preamble + assertParseFailure(parser, + PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/vimification/logic/parser/AddressBookParserTest.java similarity index 64% rename from src/test/java/seedu/address/logic/parser/AddressBookParserTest.java rename to src/test/java/vimification/logic/parser/AddressBookParserTest.java index d9659205b57..13f71ac5384 100644 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ b/src/test/java/vimification/logic/parser/AddressBookParserTest.java @@ -1,11 +1,11 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static vimification.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static vimification.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static vimification.testutil.Assert.assertThrows; +import static vimification.testutil.TypicalIndexes.INDEX_FIRST_PERSON; import java.util.Arrays; import java.util.List; @@ -13,21 +13,21 @@ import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.ClearCommand; -import seedu.address.logic.commands.DeleteCommand; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.logic.commands.ExitCommand; -import seedu.address.logic.commands.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; -import seedu.address.testutil.PersonUtil; +import vimification.logic.commands.AddCommand; +import vimification.logic.commands.ClearCommand; +import vimification.logic.commands.DeleteCommand; +import vimification.logic.commands.EditCommand; +import vimification.logic.commands.EditCommand.EditPersonDescriptor; +import vimification.logic.commands.ExitCommand; +import vimification.logic.commands.FindCommand; +import vimification.logic.commands.HelpCommand; +import vimification.logic.commands.ListCommand; +import vimification.logic.parser.exceptions.ParseException; +import vimification.model.person.NameContainsKeywordsPredicate; +import vimification.model.person.Person; +import vimification.testutil.EditPersonDescriptorBuilder; +import vimification.testutil.PersonBuilder; +import vimification.testutil.PersonUtil; public class AddressBookParserTest { @@ -58,7 +58,8 @@ public void parseCommand_edit() throws Exception { Person person = new PersonBuilder().build(); EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build(); EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " " - + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor)); + + INDEX_FIRST_PERSON.getOneBased() + " " + + PersonUtil.getEditPersonDescriptorDetails(descriptor)); assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command); } @@ -72,7 +73,8 @@ public void parseCommand_exit() throws Exception { public void parseCommand_find() throws Exception { List keywords = Arrays.asList("foo", "bar", "baz"); FindCommand command = (FindCommand) parser.parseCommand( - FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); + FindCommand.COMMAND_WORD + " " + + keywords.stream().collect(Collectors.joining(" "))); assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); } @@ -90,12 +92,14 @@ public void parseCommand_list() throws Exception { @Test public void parseCommand_unrecognisedInput_throwsParseException() { - assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () - -> parser.parseCommand("")); + assertThrows(ParseException.class, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), + () -> parser.parseCommand("")); } @Test public void parseCommand_unknownCommand_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand")); + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, + () -> parser.parseCommand("unknownCommand")); } } diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/vimification/logic/parser/ArgumentTokenizerTest.java similarity index 92% rename from src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java rename to src/test/java/vimification/logic/parser/ArgumentTokenizerTest.java index c97308935f5..0b59063e2c3 100644 --- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java +++ b/src/test/java/vimification/logic/parser/ArgumentTokenizerTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -32,10 +32,12 @@ private void assertPreambleEmpty(ArgumentMultimap argMultimap) { } /** - * Asserts all the arguments in {@code argMultimap} with {@code prefix} match the {@code expectedValues} - * and only the last value is returned upon calling {@code ArgumentMultimap#getValue(Prefix)}. + * Asserts all the arguments in {@code argMultimap} with {@code prefix} match the + * {@code expectedValues} and only the last value is returned upon calling + * {@code ArgumentMultimap#getValue(Prefix)}. */ - private void assertArgumentPresent(ArgumentMultimap argMultimap, Prefix prefix, String... expectedValues) { + private void assertArgumentPresent(ArgumentMultimap argMultimap, Prefix prefix, + String... expectedValues) { // Verify the last value is returned assertEquals(expectedValues[expectedValues.length - 1], argMultimap.getValue(prefix).get()); @@ -112,13 +114,15 @@ public void tokenize_multipleArguments() { argsString = unknownPrefix + "some value"; argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); assertArgumentAbsent(argMultimap, unknownPrefix); - assertPreamblePresent(argMultimap, argsString); // Unknown prefix is taken as part of preamble + assertPreamblePresent(argMultimap, argsString); // Unknown prefix is taken as part of + // preamble } @Test public void tokenize_multipleArgumentsWithRepeats() { // Two arguments repeated, some have empty values - String argsString = "SomePreambleString -t dashT-Value ^Q ^Q -t another dashT value p/ pSlash value -t"; + String argsString = + "SomePreambleString -t dashT-Value ^Q ^Q -t another dashT value p/ pSlash value -t"; ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ); assertPreamblePresent(argMultimap, "SomePreambleString"); assertArgumentPresent(argMultimap, pSlash, "pSlash value"); diff --git a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java b/src/test/java/vimification/logic/parser/CommandParserTestUtil.java similarity index 72% rename from src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java rename to src/test/java/vimification/logic/parser/CommandParserTestUtil.java index 9bf1ccf1cef..7ba1674ac71 100644 --- a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java +++ b/src/test/java/vimification/logic/parser/CommandParserTestUtil.java @@ -1,9 +1,9 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import vimification.logic.commands.Command; +import vimification.logic.parser.exceptions.ParseException; /** * Contains helper methods for testing command parsers. @@ -11,8 +11,8 @@ public class CommandParserTestUtil { /** - * Asserts that the parsing of {@code userInput} by {@code parser} is successful and the command created - * equals to {@code expectedCommand}. + * Asserts that the parsing of {@code userInput} by {@code parser} is successful and the command + * created equals to {@code expectedCommand}. */ public static void assertParseSuccess(Parser parser, String userInput, Command expectedCommand) { @@ -25,10 +25,11 @@ public static void assertParseSuccess(Parser parser, String u } /** - * Asserts that the parsing of {@code userInput} by {@code parser} is unsuccessful and the error message - * equals to {@code expectedMessage}. + * Asserts that the parsing of {@code userInput} by {@code parser} is unsuccessful and the error + * message equals to {@code expectedMessage}. */ - public static void assertParseFailure(Parser parser, String userInput, String expectedMessage) { + public static void assertParseFailure(Parser parser, String userInput, + String expectedMessage) { try { parser.parse(userInput); throw new AssertionError("The expected ParseException was not thrown."); diff --git a/src/test/java/vimification/logic/parser/DeleteCommandParserTest.java b/src/test/java/vimification/logic/parser/DeleteCommandParserTest.java new file mode 100644 index 00000000000..68f9f1a30b2 --- /dev/null +++ b/src/test/java/vimification/logic/parser/DeleteCommandParserTest.java @@ -0,0 +1,32 @@ +package vimification.logic.parser; + +import static vimification.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static vimification.logic.parser.CommandParserTestUtil.assertParseFailure; +import static vimification.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static vimification.testutil.TypicalIndexes.INDEX_FIRST_PERSON; + +import org.junit.jupiter.api.Test; + +import vimification.logic.commands.DeleteCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations outside of + * the DeleteCommand code. For example, inputs "1" and "1 abc" take the same path through the + * DeleteCommand, and therefore we test only one of them. The path variation for those two cases + * occur inside the ParserUtil, and therefore should be covered by the ParserUtilTest. + */ +public class DeleteCommandParserTest { + + private DeleteCommandParser parser = new DeleteCommandParser(); + + @Test + public void parse_validArgs_returnsDeleteCommand() { + assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/vimification/logic/parser/EditCommandParserTest.java similarity index 55% rename from src/test/java/seedu/address/logic/parser/EditCommandParserTest.java rename to src/test/java/vimification/logic/parser/EditCommandParserTest.java index 2ff31522486..4f86ce99f78 100644 --- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java +++ b/src/test/java/vimification/logic/parser/EditCommandParserTest.java @@ -1,47 +1,47 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON; +package vimification.logic.parser; + +import static vimification.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static vimification.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; +import static vimification.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; +import static vimification.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; +import static vimification.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; +import static vimification.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +import static vimification.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; +import static vimification.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static vimification.logic.commands.CommandTestUtil.NAME_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +import static vimification.logic.commands.CommandTestUtil.PHONE_DESC_BOB; +import static vimification.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; +import static vimification.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; +import static vimification.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; +import static vimification.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; +import static vimification.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_NAME_AMY; +import static vimification.logic.commands.CommandTestUtil.VALID_PHONE_AMY; +import static vimification.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static vimification.logic.parser.CliSyntax.PREFIX_TAG; +import static vimification.logic.parser.CommandParserTestUtil.assertParseFailure; +import static vimification.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static vimification.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static vimification.testutil.TypicalIndexes.INDEX_SECOND_PERSON; +import static vimification.testutil.TypicalIndexes.INDEX_THIRD_PERSON; import org.junit.jupiter.api.Test; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.EditPersonDescriptorBuilder; +import vimification.commons.core.index.Index; +import vimification.logic.commands.EditCommand; +import vimification.logic.commands.EditCommand.EditPersonDescriptor; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Phone; +import vimification.model.tag.Tag; +import vimification.testutil.EditPersonDescriptorBuilder; public class EditCommandParserTest { @@ -81,27 +81,39 @@ public void parse_invalidPreamble_failure() { @Test public void parse_invalidValue_failure() { - assertParseFailure(parser, "1" + INVALID_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid name - assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone - assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid email - assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address + assertParseFailure(parser, "1" + INVALID_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid + // name + assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid + // phone + assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid + // email + assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid + // address assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag // invalid phone followed by valid email - assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, Phone.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, + Phone.MESSAGE_CONSTRAINTS); - // valid phone followed by invalid phone. The test case for invalid phone followed by valid phone + // valid phone followed by invalid phone. The test case for invalid phone followed by valid + // phone // is tested at {@code parse_invalidValueFollowedByValidValue_success()} - assertParseFailure(parser, "1" + PHONE_DESC_BOB + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + PHONE_DESC_BOB + INVALID_PHONE_DESC, + Phone.MESSAGE_CONSTRAINTS); - // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited, + // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being + // edited, // parsing it together with a valid tag results in error - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, + Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, + Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, + Tag.MESSAGE_CONSTRAINTS); // multiple invalid values, but only the first invalid value is captured - assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY, + assertParseFailure(parser, + "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY, Name.MESSAGE_CONSTRAINTS); } @@ -112,7 +124,8 @@ public void parse_allFieldsSpecified_success() { + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND; EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) + .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY) + .withAddress(VALID_ADDRESS_AMY) .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); @@ -124,8 +137,9 @@ public void parse_someFieldsSpecified_success() { Index targetIndex = INDEX_FIRST_PERSON; String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + EMAIL_DESC_AMY; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_AMY).build(); + EditPersonDescriptor descriptor = + new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) + .withEmail(VALID_EMAIL_AMY).build(); EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); @@ -136,7 +150,8 @@ public void parse_oneFieldSpecified_success() { // name Index targetIndex = INDEX_THIRD_PERSON; String userInput = targetIndex.getOneBased() + NAME_DESC_AMY; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build(); + EditPersonDescriptor descriptor = + new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build(); EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); @@ -168,13 +183,17 @@ public void parse_oneFieldSpecified_success() { @Test public void parse_multipleRepeatedFields_acceptsLast() { Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY - + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND - + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); + String userInput = + targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + + TAG_DESC_FRIEND + + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND; + + EditPersonDescriptor descriptor = + new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) + .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB) + .withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) + .build(); EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); @@ -185,14 +204,17 @@ public void parse_invalidValueFollowedByValidValue_success() { // no other valid values specified Index targetIndex = INDEX_FIRST_PERSON; String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).build(); + EditPersonDescriptor descriptor = + new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).build(); EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); // other valid values specified - userInput = targetIndex.getOneBased() + EMAIL_DESC_BOB + INVALID_PHONE_DESC + ADDRESS_DESC_BOB - + PHONE_DESC_BOB; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) + userInput = + targetIndex.getOneBased() + EMAIL_DESC_BOB + INVALID_PHONE_DESC + ADDRESS_DESC_BOB + + PHONE_DESC_BOB; + descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) + .withEmail(VALID_EMAIL_BOB) .withAddress(VALID_ADDRESS_BOB).build(); expectedCommand = new EditCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/vimification/logic/parser/FindCommandParserTest.java similarity index 57% rename from src/test/java/seedu/address/logic/parser/FindCommandParserTest.java rename to src/test/java/vimification/logic/parser/FindCommandParserTest.java index 70f4f0e79c4..75ba2e61f26 100644 --- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java +++ b/src/test/java/vimification/logic/parser/FindCommandParserTest.java @@ -1,15 +1,15 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static vimification.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static vimification.logic.parser.CommandParserTestUtil.assertParseFailure; +import static vimification.logic.parser.CommandParserTestUtil.assertParseSuccess; import java.util.Arrays; import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.FindCommand; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import vimification.logic.commands.FindCommand; +import vimification.model.person.NameContainsKeywordsPredicate; public class FindCommandParserTest { @@ -17,7 +17,8 @@ public class FindCommandParserTest { @Test public void parse_emptyArg_throwsParseException() { - assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); } @Test diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/vimification/logic/parser/ParserUtilTest.java similarity index 89% rename from src/test/java/seedu/address/logic/parser/ParserUtilTest.java rename to src/test/java/vimification/logic/parser/ParserUtilTest.java index 4256788b1a7..589e1bb3f4f 100644 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ b/src/test/java/vimification/logic/parser/ParserUtilTest.java @@ -1,10 +1,10 @@ -package seedu.address.logic.parser; +package vimification.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static vimification.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; +import static vimification.testutil.Assert.assertThrows; +import static vimification.testutil.TypicalIndexes.INDEX_FIRST_PERSON; import java.util.Arrays; import java.util.Collections; @@ -13,12 +13,12 @@ import org.junit.jupiter.api.Test; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import vimification.logic.parser.exceptions.ParseException; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Phone; +import vimification.model.tag.Tag; public class ParserUtilTest { private static final String INVALID_NAME = "R@chel"; @@ -43,8 +43,8 @@ public void parseIndex_invalidInput_throwsParseException() { @Test public void parseIndex_outOfRangeInput_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, () - -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); + assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, + () -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); } @Test @@ -178,7 +178,8 @@ public void parseTags_null_throwsNullPointerException() { @Test public void parseTags_collectionWithInvalidTags_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); + assertThrows(ParseException.class, + () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); } @Test @@ -189,7 +190,8 @@ public void parseTags_emptyCollection_returnsEmptySet() throws Exception { @Test public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2)); - Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); + Set expectedTagSet = + new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); assertEquals(expectedTagSet, actualTagSet); } diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/vimification/model/AddressBookTest.java similarity index 73% rename from src/test/java/seedu/address/model/AddressBookTest.java rename to src/test/java/vimification/model/AddressBookTest.java index 87782528ecd..c9e6d258200 100644 --- a/src/test/java/seedu/address/model/AddressBookTest.java +++ b/src/test/java/vimification/model/AddressBookTest.java @@ -1,13 +1,13 @@ -package seedu.address.model; +package vimification.model; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static vimification.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static vimification.testutil.Assert.assertThrows; +import static vimification.testutil.TypicalPersons.ALICE; +import static vimification.testutil.TypicalPersons.getTypicalAddressBook; import java.util.Arrays; import java.util.Collection; @@ -18,9 +18,9 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.testutil.PersonBuilder; +import vimification.model.person.Person; +import vimification.model.person.exceptions.DuplicatePersonException; +import vimification.testutil.PersonBuilder; public class AddressBookTest { @@ -46,8 +46,9 @@ public void resetData_withValidReadOnlyAddressBook_replacesData() { @Test public void resetData_withDuplicatePersons_throwsDuplicatePersonException() { // Two persons with the same identity fields - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); + Person editedAlice = + new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) + .build(); List newPersons = Arrays.asList(ALICE, editedAlice); AddressBookStub newData = new AddressBookStub(newPersons); @@ -73,14 +74,16 @@ public void hasPerson_personInAddressBook_returnsTrue() { @Test public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() { addressBook.addPerson(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); + Person editedAlice = + new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) + .build(); assertTrue(addressBook.hasPerson(editedAlice)); } @Test public void getPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0)); + assertThrows(UnsupportedOperationException.class, + () -> addressBook.getPersonList().remove(0)); } /** diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/vimification/model/ModelManagerTest.java similarity index 84% rename from src/test/java/seedu/address/model/ModelManagerTest.java rename to src/test/java/vimification/model/ModelManagerTest.java index 2cf1418d116..c537ecdfae6 100644 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ b/src/test/java/vimification/model/ModelManagerTest.java @@ -1,12 +1,12 @@ -package seedu.address.model; +package vimification.model; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BENSON; +import static vimification.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static vimification.testutil.Assert.assertThrows; +import static vimification.testutil.TypicalPersons.ALICE; +import static vimification.testutil.TypicalPersons.BENSON; import java.nio.file.Path; import java.nio.file.Paths; @@ -14,9 +14,9 @@ import org.junit.jupiter.api.Test; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.testutil.AddressBookBuilder; +import vimification.commons.core.GuiSettings; +import vimification.model.person.NameContainsKeywordsPredicate; +import vimification.testutil.AddressBookBuilder; public class ModelManagerTest { @@ -90,12 +90,14 @@ public void hasPerson_personInAddressBook_returnsTrue() { @Test public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0)); + assertThrows(UnsupportedOperationException.class, + () -> modelManager.getFilteredPersonList().remove(0)); } @Test public void equals() { - AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build(); + AddressBook addressBook = + new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build(); AddressBook differentAddressBook = new AddressBook(); UserPrefs userPrefs = new UserPrefs(); @@ -118,7 +120,8 @@ public void equals() { // different filteredList -> returns false String[] keywords = ALICE.getName().fullName.split("\\s+"); - modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); + modelManager.updateFilteredPersonList( + new NameContainsKeywordsPredicate(Arrays.asList(keywords))); assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs))); // resets modelManager to initial state for upcoming tests diff --git a/src/test/java/seedu/address/model/UserPrefsTest.java b/src/test/java/vimification/model/UserPrefsTest.java similarity index 86% rename from src/test/java/seedu/address/model/UserPrefsTest.java rename to src/test/java/vimification/model/UserPrefsTest.java index b1307a70d52..c5a467d69a9 100644 --- a/src/test/java/seedu/address/model/UserPrefsTest.java +++ b/src/test/java/vimification/model/UserPrefsTest.java @@ -1,6 +1,6 @@ -package seedu.address.model; +package vimification.model; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/model/person/AddressTest.java b/src/test/java/vimification/model/person/AddressTest.java similarity index 76% rename from src/test/java/seedu/address/model/person/AddressTest.java rename to src/test/java/vimification/model/person/AddressTest.java index dcd3be87b3a..f655d27853e 100644 --- a/src/test/java/seedu/address/model/person/AddressTest.java +++ b/src/test/java/vimification/model/person/AddressTest.java @@ -1,8 +1,8 @@ -package seedu.address.model.person; +package vimification.model.person; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -31,6 +31,8 @@ public void isValidAddress() { // valid addresses assertTrue(Address.isValidAddress("Blk 456, Den Road, #01-355")); assertTrue(Address.isValidAddress("-")); // one character - assertTrue(Address.isValidAddress("Leng Inc; 1234 Market St; San Francisco CA 2349879; USA")); // long address + assertTrue( + Address.isValidAddress("Leng Inc; 1234 Market St; San Francisco CA 2349879; USA")); // long + // address } } diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/vimification/model/person/EmailTest.java similarity index 77% rename from src/test/java/seedu/address/model/person/EmailTest.java rename to src/test/java/vimification/model/person/EmailTest.java index bbcc6c8c98e..e29ded139d1 100644 --- a/src/test/java/seedu/address/model/person/EmailTest.java +++ b/src/test/java/vimification/model/person/EmailTest.java @@ -1,8 +1,8 @@ -package seedu.address.model.person; +package vimification.model.person; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -42,15 +42,20 @@ public void isValidEmail() { assertFalse(Email.isValidEmail("peterjack@example.com ")); // trailing space assertFalse(Email.isValidEmail("peterjack@@example.com")); // double '@' symbol assertFalse(Email.isValidEmail("peter@jack@example.com")); // '@' symbol in local part - assertFalse(Email.isValidEmail("-peterjack@example.com")); // local part starts with a hyphen + assertFalse(Email.isValidEmail("-peterjack@example.com")); // local part starts with a + // hyphen assertFalse(Email.isValidEmail("peterjack-@example.com")); // local part ends with a hyphen - assertFalse(Email.isValidEmail("peter..jack@example.com")); // local part has two consecutive periods + assertFalse(Email.isValidEmail("peter..jack@example.com")); // local part has two + // consecutive periods assertFalse(Email.isValidEmail("peterjack@example@com")); // '@' symbol in domain name - assertFalse(Email.isValidEmail("peterjack@.example.com")); // domain name starts with a period + assertFalse(Email.isValidEmail("peterjack@.example.com")); // domain name starts with a + // period assertFalse(Email.isValidEmail("peterjack@example.com.")); // domain name ends with a period - assertFalse(Email.isValidEmail("peterjack@-example.com")); // domain name starts with a hyphen + assertFalse(Email.isValidEmail("peterjack@-example.com")); // domain name starts with a + // hyphen assertFalse(Email.isValidEmail("peterjack@example.com-")); // domain name ends with a hyphen - assertFalse(Email.isValidEmail("peterjack@example.c")); // top level domain has less than two chars + assertFalse(Email.isValidEmail("peterjack@example.c")); // top level domain has less than + // two chars // valid email assertTrue(Email.isValidEmail("PeterJack_1190@example.com")); // underscore in local part @@ -60,9 +65,12 @@ public void isValidEmail() { assertTrue(Email.isValidEmail("a@bc")); // minimal assertTrue(Email.isValidEmail("test@localhost")); // alphabets only assertTrue(Email.isValidEmail("123@145")); // numeric local part and domain name - assertTrue(Email.isValidEmail("a1+be.d@example1.com")); // mixture of alphanumeric and special characters - assertTrue(Email.isValidEmail("peter_jack@very-very-very-long-example.com")); // long domain name - assertTrue(Email.isValidEmail("if.you.dream.it_you.can.do.it@example.com")); // long local part + assertTrue(Email.isValidEmail("a1+be.d@example1.com")); // mixture of alphanumeric and + // special characters + assertTrue(Email.isValidEmail("peter_jack@very-very-very-long-example.com")); // long domain + // name + assertTrue(Email.isValidEmail("if.you.dream.it_you.can.do.it@example.com")); // long local + // part assertTrue(Email.isValidEmail("e1234567@u.nus.edu")); // more than one period in domain } } diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/vimification/model/person/NameContainsKeywordsPredicateTest.java similarity index 73% rename from src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java rename to src/test/java/vimification/model/person/NameContainsKeywordsPredicateTest.java index f136664e017..3326d617710 100644 --- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java +++ b/src/test/java/vimification/model/person/NameContainsKeywordsPredicateTest.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package vimification.model.person; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.PersonBuilder; +import vimification.testutil.PersonBuilder; public class NameContainsKeywordsPredicateTest { @@ -18,14 +18,17 @@ public void equals() { List firstPredicateKeywordList = Collections.singletonList("first"); List secondPredicateKeywordList = Arrays.asList("first", "second"); - NameContainsKeywordsPredicate firstPredicate = new NameContainsKeywordsPredicate(firstPredicateKeywordList); - NameContainsKeywordsPredicate secondPredicate = new NameContainsKeywordsPredicate(secondPredicateKeywordList); + NameContainsKeywordsPredicate firstPredicate = + new NameContainsKeywordsPredicate(firstPredicateKeywordList); + NameContainsKeywordsPredicate secondPredicate = + new NameContainsKeywordsPredicate(secondPredicateKeywordList); // same object -> returns true assertTrue(firstPredicate.equals(firstPredicate)); // same values -> returns true - NameContainsKeywordsPredicate firstPredicateCopy = new NameContainsKeywordsPredicate(firstPredicateKeywordList); + NameContainsKeywordsPredicate firstPredicateCopy = + new NameContainsKeywordsPredicate(firstPredicateKeywordList); assertTrue(firstPredicate.equals(firstPredicateCopy)); // different types -> returns false @@ -41,7 +44,8 @@ public void equals() { @Test public void test_nameContainsKeywords_returnsTrue() { // One keyword - NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.singletonList("Alice")); + NameContainsKeywordsPredicate predicate = + new NameContainsKeywordsPredicate(Collections.singletonList("Alice")); assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); // Multiple keywords @@ -60,7 +64,8 @@ public void test_nameContainsKeywords_returnsTrue() { @Test public void test_nameDoesNotContainKeywords_returnsFalse() { // Zero keywords - NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.emptyList()); + NameContainsKeywordsPredicate predicate = + new NameContainsKeywordsPredicate(Collections.emptyList()); assertFalse(predicate.test(new PersonBuilder().withName("Alice").build())); // Non-matching keyword @@ -68,7 +73,8 @@ public void test_nameDoesNotContainKeywords_returnsFalse() { assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build())); // Keywords match phone, email and address, but does not match name - predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street")); + predicate = new NameContainsKeywordsPredicate( + Arrays.asList("12345", "alice@email.com", "Main", "Street")); assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345") .withEmail("alice@email.com").withAddress("Main Street").build())); } diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/vimification/model/person/NameTest.java similarity index 93% rename from src/test/java/seedu/address/model/person/NameTest.java rename to src/test/java/vimification/model/person/NameTest.java index c9801392874..f6ae6cb087e 100644 --- a/src/test/java/seedu/address/model/person/NameTest.java +++ b/src/test/java/vimification/model/person/NameTest.java @@ -1,8 +1,8 @@ -package seedu.address.model.person; +package vimification.model.person; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/vimification/model/person/PersonTest.java similarity index 77% rename from src/test/java/seedu/address/model/person/PersonTest.java rename to src/test/java/vimification/model/person/PersonTest.java index b29c097cfd4..a4383f7a8cc 100644 --- a/src/test/java/seedu/address/model/person/PersonTest.java +++ b/src/test/java/vimification/model/person/PersonTest.java @@ -1,19 +1,19 @@ -package seedu.address.model.person; +package vimification.model.person; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static vimification.testutil.Assert.assertThrows; +import static vimification.testutil.TypicalPersons.ALICE; +import static vimification.testutil.TypicalPersons.BOB; import org.junit.jupiter.api.Test; -import seedu.address.testutil.PersonBuilder; +import vimification.testutil.PersonBuilder; public class PersonTest { @@ -32,8 +32,9 @@ public void isSamePerson() { assertFalse(ALICE.isSamePerson(null)); // same name, all other attributes different -> returns true - Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND).build(); + Person editedAlice = + new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) + .withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND).build(); assertTrue(ALICE.isSamePerson(editedAlice)); // different name, all other attributes same -> returns false diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/vimification/model/person/PhoneTest.java similarity index 93% rename from src/test/java/seedu/address/model/person/PhoneTest.java rename to src/test/java/vimification/model/person/PhoneTest.java index 8dd52766a5f..aa7941e0967 100644 --- a/src/test/java/seedu/address/model/person/PhoneTest.java +++ b/src/test/java/vimification/model/person/PhoneTest.java @@ -1,8 +1,8 @@ -package seedu.address.model.person; +package vimification.model.person; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/vimification/model/person/UniquePersonListTest.java similarity index 79% rename from src/test/java/seedu/address/model/person/UniquePersonListTest.java rename to src/test/java/vimification/model/person/UniquePersonListTest.java index 1cc5fe9e0fe..9fa4fa228db 100644 --- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java +++ b/src/test/java/vimification/model/person/UniquePersonListTest.java @@ -1,13 +1,13 @@ -package seedu.address.model.person; +package vimification.model.person; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static vimification.testutil.Assert.assertThrows; +import static vimification.testutil.TypicalPersons.ALICE; +import static vimification.testutil.TypicalPersons.BOB; import java.util.Arrays; import java.util.Collections; @@ -15,9 +15,9 @@ import org.junit.jupiter.api.Test; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; -import seedu.address.testutil.PersonBuilder; +import vimification.model.person.exceptions.DuplicatePersonException; +import vimification.model.person.exceptions.PersonNotFoundException; +import vimification.testutil.PersonBuilder; public class UniquePersonListTest { @@ -42,8 +42,9 @@ public void contains_personInList_returnsTrue() { @Test public void contains_personWithSameIdentityFieldsInList_returnsTrue() { uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); + Person editedAlice = + new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) + .build(); assertTrue(uniquePersonList.contains(editedAlice)); } @@ -85,8 +86,9 @@ public void setPerson_editedPersonIsSamePerson_success() { @Test public void setPerson_editedPersonHasSameIdentity_success() { uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); + Person editedAlice = + new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) + .build(); uniquePersonList.setPerson(ALICE, editedAlice); UniquePersonList expectedUniquePersonList = new UniquePersonList(); expectedUniquePersonList.add(editedAlice); @@ -129,7 +131,8 @@ public void remove_existingPerson_removesPerson() { @Test public void setPersons_nullUniquePersonList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((UniquePersonList) null)); + assertThrows(NullPointerException.class, + () -> uniquePersonList.setPersons((UniquePersonList) null)); } @Test @@ -143,7 +146,8 @@ public void setPersons_uniquePersonList_replacesOwnListWithProvidedUniquePersonL @Test public void setPersons_nullList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((List) null)); + assertThrows(NullPointerException.class, + () -> uniquePersonList.setPersons((List) null)); } @Test @@ -159,12 +163,13 @@ public void setPersons_list_replacesOwnListWithProvidedList() { @Test public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() { List listWithDuplicatePersons = Arrays.asList(ALICE, ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPersons(listWithDuplicatePersons)); + assertThrows(DuplicatePersonException.class, + () -> uniquePersonList.setPersons(listWithDuplicatePersons)); } @Test public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () - -> uniquePersonList.asUnmodifiableObservableList().remove(0)); + assertThrows(UnsupportedOperationException.class, + () -> uniquePersonList.asUnmodifiableObservableList().remove(0)); } } diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/vimification/model/tag/TagTest.java similarity index 86% rename from src/test/java/seedu/address/model/tag/TagTest.java rename to src/test/java/vimification/model/tag/TagTest.java index 64d07d79ee2..97725e1f825 100644 --- a/src/test/java/seedu/address/model/tag/TagTest.java +++ b/src/test/java/vimification/model/tag/TagTest.java @@ -1,6 +1,6 @@ -package seedu.address.model.tag; +package vimification.model.tag; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/vimification/storage/JsonAdaptedPersonTest.java similarity index 67% rename from src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java rename to src/test/java/vimification/storage/JsonAdaptedPersonTest.java index 83b11331cdb..80ea44709a0 100644 --- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java +++ b/src/test/java/vimification/storage/JsonAdaptedPersonTest.java @@ -1,9 +1,9 @@ -package seedu.address.storage; +package vimification.storage; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.BENSON; +import static vimification.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT; +import static vimification.testutil.Assert.assertThrows; +import static vimification.testutil.TypicalPersons.BENSON; import java.util.ArrayList; import java.util.List; @@ -11,11 +11,11 @@ import org.junit.jupiter.api.Test; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; +import vimification.commons.exceptions.IllegalValueException; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Phone; public class JsonAdaptedPersonTest { private static final String INVALID_NAME = "R@chel"; @@ -41,60 +41,72 @@ public void toModelType_validPersonDetails_returnsPerson() throws Exception { @Test public void toModelType_invalidName_throwsIllegalValueException() { JsonAdaptedPerson person = - new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); + new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, + VALID_TAGS); String expectedMessage = Name.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @Test public void toModelType_nullName_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); + JsonAdaptedPerson person = + new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); + String expectedMessage = + String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @Test public void toModelType_invalidPhone_throwsIllegalValueException() { JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); + new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, + VALID_TAGS); String expectedMessage = Phone.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @Test public void toModelType_nullPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()); + JsonAdaptedPerson person = + new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); + String expectedMessage = + String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @Test public void toModelType_invalidEmail_throwsIllegalValueException() { JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS); + new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, + VALID_TAGS); String expectedMessage = Email.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @Test public void toModelType_nullEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()); + JsonAdaptedPerson person = + new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS); + String expectedMessage = + String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @Test public void toModelType_invalidAddress_throwsIllegalValueException() { JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS); + new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, + VALID_TAGS); String expectedMessage = Address.MESSAGE_CONSTRAINTS; assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @Test public void toModelType_nullAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()); + JsonAdaptedPerson person = + new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS); + String expectedMessage = + String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @@ -103,7 +115,8 @@ public void toModelType_invalidTags_throwsIllegalValueException() { List invalidTags = new ArrayList<>(VALID_TAGS); invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags); + new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, + invalidTags); assertThrows(IllegalValueException.class, person::toModelType); } diff --git a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java b/src/test/java/vimification/storage/JsonAddressBookStorageTest.java similarity index 74% rename from src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java rename to src/test/java/vimification/storage/JsonAddressBookStorageTest.java index ac3c3af9566..28addc6a95a 100644 --- a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java +++ b/src/test/java/vimification/storage/JsonAddressBookStorageTest.java @@ -1,12 +1,12 @@ -package seedu.address.storage; +package vimification.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.HOON; -import static seedu.address.testutil.TypicalPersons.IDA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static vimification.testutil.Assert.assertThrows; +import static vimification.testutil.TypicalPersons.ALICE; +import static vimification.testutil.TypicalPersons.HOON; +import static vimification.testutil.TypicalPersons.IDA; +import static vimification.testutil.TypicalPersons.getTypicalAddressBook; import java.io.IOException; import java.nio.file.Path; @@ -15,12 +15,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; +import vimification.commons.exceptions.DataConversionException; +import vimification.model.AddressBook; +import vimification.model.ReadOnlyAddressBook; public class JsonAddressBookStorageTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonAddressBookStorageTest"); + private static final Path TEST_DATA_FOLDER = + Paths.get("src", "test", "data", "JsonAddressBookStorageTest"); @TempDir public Path testFolder; @@ -30,8 +31,10 @@ public void readAddressBook_nullFilePath_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> readAddressBook(null)); } - private java.util.Optional readAddressBook(String filePath) throws Exception { - return new JsonAddressBookStorage(Paths.get(filePath)).readAddressBook(addToTestDataPathIfNotNull(filePath)); + private java.util.Optional readAddressBook(String filePath) + throws Exception { + return new JsonAddressBookStorage(Paths.get(filePath)) + .readAddressBook(addToTestDataPathIfNotNull(filePath)); } private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { @@ -47,17 +50,20 @@ public void read_missingFile_emptyResult() throws Exception { @Test public void read_notJsonFormat_exceptionThrown() { - assertThrows(DataConversionException.class, () -> readAddressBook("notJsonFormatAddressBook.json")); + assertThrows(DataConversionException.class, + () -> readAddressBook("notJsonFormatAddressBook.json")); } @Test public void readAddressBook_invalidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidPersonAddressBook.json")); + assertThrows(DataConversionException.class, + () -> readAddressBook("invalidPersonAddressBook.json")); } @Test public void readAddressBook_invalidAndValidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidAndValidPersonAddressBook.json")); + assertThrows(DataConversionException.class, + () -> readAddressBook("invalidAndValidPersonAddressBook.json")); } @Test diff --git a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java b/src/test/java/vimification/storage/JsonSerializableAddressBookTest.java similarity index 59% rename from src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java rename to src/test/java/vimification/storage/JsonSerializableAddressBookTest.java index 188c9058d20..b32b09acc8c 100644 --- a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java +++ b/src/test/java/vimification/storage/JsonSerializableAddressBookTest.java @@ -1,24 +1,28 @@ -package seedu.address.storage; +package vimification.storage; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import java.nio.file.Path; import java.nio.file.Paths; import org.junit.jupiter.api.Test; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.AddressBook; -import seedu.address.testutil.TypicalPersons; +import vimification.commons.exceptions.IllegalValueException; +import vimification.commons.util.JsonUtil; +import vimification.model.AddressBook; +import vimification.testutil.TypicalPersons; public class JsonSerializableAddressBookTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableAddressBookTest"); - private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalPersonsAddressBook.json"); - private static final Path INVALID_PERSON_FILE = TEST_DATA_FOLDER.resolve("invalidPersonAddressBook.json"); - private static final Path DUPLICATE_PERSON_FILE = TEST_DATA_FOLDER.resolve("duplicatePersonAddressBook.json"); + private static final Path TEST_DATA_FOLDER = + Paths.get("src", "test", "data", "JsonSerializableAddressBookTest"); + private static final Path TYPICAL_PERSONS_FILE = + TEST_DATA_FOLDER.resolve("typicalPersonsAddressBook.json"); + private static final Path INVALID_PERSON_FILE = + TEST_DATA_FOLDER.resolve("invalidPersonAddressBook.json"); + private static final Path DUPLICATE_PERSON_FILE = + TEST_DATA_FOLDER.resolve("duplicatePersonAddressBook.json"); @Test public void toModelType_typicalPersonsFile_success() throws Exception { @@ -40,7 +44,8 @@ public void toModelType_invalidPersonFile_throwsIllegalValueException() throws E public void toModelType_duplicatePersons_throwsIllegalValueException() throws Exception { JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_PERSON_FILE, JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, JsonSerializableAddressBook.MESSAGE_DUPLICATE_PERSON, + assertThrows(IllegalValueException.class, + JsonSerializableAddressBook.MESSAGE_DUPLICATE_PERSON, dataFromFile::toModelType); } diff --git a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java b/src/test/java/vimification/storage/JsonUserPrefsStorageTest.java similarity index 84% rename from src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java rename to src/test/java/vimification/storage/JsonUserPrefsStorageTest.java index 16f33f4a6bb..f67c441edb7 100644 --- a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java +++ b/src/test/java/vimification/storage/JsonUserPrefsStorageTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package vimification.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -12,13 +12,14 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.UserPrefs; +import vimification.commons.core.GuiSettings; +import vimification.commons.exceptions.DataConversionException; +import vimification.model.UserPrefs; public class JsonUserPrefsStorageTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonUserPrefsStorageTest"); + private static final Path TEST_DATA_FOLDER = + Paths.get("src", "test", "data", "JsonUserPrefsStorageTest"); @TempDir public Path testFolder; @@ -28,7 +29,8 @@ public void readUserPrefs_nullFilePath_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> readUserPrefs(null)); } - private Optional readUserPrefs(String userPrefsFileInTestDataFolder) throws DataConversionException { + private Optional readUserPrefs(String userPrefsFileInTestDataFolder) + throws DataConversionException { Path prefsFilePath = addToTestDataPathIfNotNull(userPrefsFileInTestDataFolder); return new JsonUserPrefsStorage(prefsFilePath).readUserPrefs(prefsFilePath); } @@ -40,7 +42,8 @@ public void readUserPrefs_missingFile_emptyResult() throws DataConversionExcepti @Test public void readUserPrefs_notJsonFormat_exceptionThrown() { - assertThrows(DataConversionException.class, () -> readUserPrefs("NotJsonFormatUserPrefs.json")); + assertThrows(DataConversionException.class, + () -> readUserPrefs("NotJsonFormatUserPrefs.json")); } private Path addToTestDataPathIfNotNull(String userPrefsFileInTestDataFolder) { @@ -57,13 +60,15 @@ public void readUserPrefs_fileInOrder_successfullyRead() throws DataConversionEx } @Test - public void readUserPrefs_valuesMissingFromFile_defaultValuesUsed() throws DataConversionException { + public void readUserPrefs_valuesMissingFromFile_defaultValuesUsed() + throws DataConversionException { UserPrefs actual = readUserPrefs("EmptyUserPrefs.json").get(); assertEquals(new UserPrefs(), actual); } @Test - public void readUserPrefs_extraValuesInFile_extraValuesIgnored() throws DataConversionException { + public void readUserPrefs_extraValuesInFile_extraValuesIgnored() + throws DataConversionException { UserPrefs expected = getTypicalUserPrefs(); UserPrefs actual = readUserPrefs("ExtraValuesUserPref.json").get(); @@ -108,12 +113,12 @@ public void saveUserPrefs_allInOrder_success() throws DataConversionException, I Path pefsFilePath = testFolder.resolve("TempPrefs.json"); JsonUserPrefsStorage jsonUserPrefsStorage = new JsonUserPrefsStorage(pefsFilePath); - //Try writing when the file doesn't exist + // Try writing when the file doesn't exist jsonUserPrefsStorage.saveUserPrefs(original); UserPrefs readBack = jsonUserPrefsStorage.readUserPrefs().get(); assertEquals(original, readBack); - //Try saving when the file exists + // Try saving when the file exists original.setGuiSettings(new GuiSettings(5, 5, 5, 5)); jsonUserPrefsStorage.saveUserPrefs(original); readBack = jsonUserPrefsStorage.readUserPrefs().get(); diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/vimification/storage/StorageManagerTest.java similarity index 68% rename from src/test/java/seedu/address/storage/StorageManagerTest.java rename to src/test/java/vimification/storage/StorageManagerTest.java index 99a16548970..e80634c0e9b 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/vimification/storage/StorageManagerTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package vimification.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static vimification.testutil.TypicalPersons.getTypicalAddressBook; import java.nio.file.Path; @@ -10,10 +10,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; +import vimification.commons.core.GuiSettings; +import vimification.model.AddressBook; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.UserPrefs; public class StorageManagerTest { @@ -24,7 +24,8 @@ public class StorageManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); + JsonAddressBookStorage addressBookStorage = + new JsonAddressBookStorage(getTempFilePath("ab")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); storageManager = new StorageManager(addressBookStorage, userPrefsStorage); } @@ -36,9 +37,9 @@ private Path getTempFilePath(String fileName) { @Test public void prefsReadSave() throws Exception { /* - * Note: This is an integration test that verifies the StorageManager is properly wired to the - * {@link JsonUserPrefsStorage} class. - * More extensive testing of UserPref saving/reading is done in {@link JsonUserPrefsStorageTest} class. + * Note: This is an integration test that verifies the StorageManager is properly wired to + * the {@link JsonUserPrefsStorage} class. More extensive testing of UserPref saving/reading + * is done in {@link JsonUserPrefsStorageTest} class. */ UserPrefs original = new UserPrefs(); original.setGuiSettings(new GuiSettings(300, 600, 4, 6)); @@ -50,9 +51,9 @@ public void prefsReadSave() throws Exception { @Test public void addressBookReadSave() throws Exception { /* - * Note: This is an integration test that verifies the StorageManager is properly wired to the - * {@link JsonAddressBookStorage} class. - * More extensive testing of UserPref saving/reading is done in {@link JsonAddressBookStorageTest} class. + * Note: This is an integration test that verifies the StorageManager is properly wired to + * the {@link JsonAddressBookStorage} class. More extensive testing of UserPref + * saving/reading is done in {@link JsonAddressBookStorageTest} class. */ AddressBook original = getTypicalAddressBook(); storageManager.saveAddressBook(original); diff --git a/src/test/java/seedu/address/testutil/AddressBookBuilder.java b/src/test/java/vimification/testutil/AddressBookBuilder.java similarity index 65% rename from src/test/java/seedu/address/testutil/AddressBookBuilder.java rename to src/test/java/vimification/testutil/AddressBookBuilder.java index d53799fd110..d0d00e992ac 100644 --- a/src/test/java/seedu/address/testutil/AddressBookBuilder.java +++ b/src/test/java/vimification/testutil/AddressBookBuilder.java @@ -1,12 +1,11 @@ -package seedu.address.testutil; +package vimification.testutil; -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; +import vimification.model.AddressBook; +import vimification.model.person.Person; /** - * A utility class to help with building Addressbook objects. - * Example usage:
- * {@code AddressBook ab = new AddressBookBuilder().withPerson("John", "Doe").build();} + * A utility class to help with building Addressbook objects. Example usage:
+ * {@code AddressBook ab = new AddressBookBuilder().withPerson("John", "Doe").build();} */ public class AddressBookBuilder { diff --git a/src/test/java/seedu/address/testutil/Assert.java b/src/test/java/vimification/testutil/Assert.java similarity index 60% rename from src/test/java/seedu/address/testutil/Assert.java rename to src/test/java/vimification/testutil/Assert.java index 9863093bd6e..ac545a86115 100644 --- a/src/test/java/seedu/address/testutil/Assert.java +++ b/src/test/java/vimification/testutil/Assert.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package vimification.testutil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.function.Executable; @@ -9,20 +9,21 @@ public class Assert { /** - * Asserts that the {@code executable} throws the {@code expectedType} Exception. - * This is a wrapper method that invokes {@link Assertions#assertThrows(Class, Executable)}, to maintain consistency - * with our custom {@link #assertThrows(Class, String, Executable)} method. - * To standardize API calls in this project, users should use this method instead of + * Asserts that the {@code executable} throws the {@code expectedType} Exception. This is a + * wrapper method that invokes {@link Assertions#assertThrows(Class, Executable)}, to maintain + * consistency with our custom {@link #assertThrows(Class, String, Executable)} method. To + * standardize API calls in this project, users should use this method instead of * {@link Assertions#assertThrows(Class, Executable)}. */ - public static void assertThrows(Class expectedType, Executable executable) { + public static void assertThrows(Class expectedType, + Executable executable) { Assertions.assertThrows(expectedType, executable); } /** - * Asserts that the {@code executable} throws the {@code expectedType} Exception with the {@code expectedMessage}. - * If there's no need for the verification of the exception's error message, call - * {@link #assertThrows(Class, Executable)} instead. + * Asserts that the {@code executable} throws the {@code expectedType} Exception with the + * {@code expectedMessage}. If there's no need for the verification of the exception's error + * message, call {@link #assertThrows(Class, Executable)} instead. * * @see #assertThrows(Class, Executable) */ diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/vimification/testutil/EditPersonDescriptorBuilder.java similarity index 84% rename from src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java rename to src/test/java/vimification/testutil/EditPersonDescriptorBuilder.java index 4584bd5044e..8ec923a537c 100644 --- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java +++ b/src/test/java/vimification/testutil/EditPersonDescriptorBuilder.java @@ -1,16 +1,16 @@ -package seedu.address.testutil; +package vimification.testutil; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import vimification.logic.commands.EditCommand.EditPersonDescriptor; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Person; +import vimification.model.person.Phone; +import vimification.model.tag.Tag; /** * A utility class to help with building EditPersonDescriptor objects. @@ -72,8 +72,8 @@ public EditPersonDescriptorBuilder withAddress(String address) { } /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code EditPersonDescriptor} - * that we are building. + * Parses the {@code tags} into a {@code Set} and set it to the + * {@code EditPersonDescriptor} that we are building. */ public EditPersonDescriptorBuilder withTags(String... tags) { Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/vimification/testutil/PersonBuilder.java similarity index 84% rename from src/test/java/seedu/address/testutil/PersonBuilder.java rename to src/test/java/vimification/testutil/PersonBuilder.java index 6be381d39ba..f89021f0559 100644 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ b/src/test/java/vimification/testutil/PersonBuilder.java @@ -1,15 +1,15 @@ -package seedu.address.testutil; +package vimification.testutil; import java.util.HashSet; import java.util.Set; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.model.util.SampleDataUtil; +import vimification.model.person.Address; +import vimification.model.person.Email; +import vimification.model.person.Name; +import vimification.model.person.Person; +import vimification.model.person.Phone; +import vimification.model.tag.Tag; +import vimification.model.util.SampleDataUtil; /** * A utility class to help with building Person objects. @@ -58,9 +58,10 @@ public PersonBuilder withName(String name) { } /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building. + * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are + * building. */ - public PersonBuilder withTags(String ... tags) { + public PersonBuilder withTags(String... tags) { this.tags = SampleDataUtil.getTagSet(tags); return this; } diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/vimification/testutil/PersonUtil.java similarity index 56% rename from src/test/java/seedu/address/testutil/PersonUtil.java rename to src/test/java/vimification/testutil/PersonUtil.java index 90849945183..a22ce41455e 100644 --- a/src/test/java/seedu/address/testutil/PersonUtil.java +++ b/src/test/java/vimification/testutil/PersonUtil.java @@ -1,17 +1,17 @@ -package seedu.address.testutil; +package vimification.testutil; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -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 vimification.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static vimification.logic.parser.CliSyntax.PREFIX_EMAIL; +import static vimification.logic.parser.CliSyntax.PREFIX_NAME; +import static vimification.logic.parser.CliSyntax.PREFIX_PHONE; +import static vimification.logic.parser.CliSyntax.PREFIX_TAG; import java.util.Set; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Person; -import seedu.address.model.tag.Tag; +import vimification.logic.commands.AddCommand; +import vimification.logic.commands.EditCommand.EditPersonDescriptor; +import vimification.model.person.Person; +import vimification.model.tag.Tag; /** * A utility class for Person. @@ -35,8 +35,7 @@ public static String getPersonDetails(Person person) { sb.append(PREFIX_EMAIL + person.getEmail().value + " "); sb.append(PREFIX_ADDRESS + person.getAddress().value + " "); person.getTags().stream().forEach( - s -> sb.append(PREFIX_TAG + s.tagName + " ") - ); + s -> sb.append(PREFIX_TAG + s.tagName + " ")); return sb.toString(); } @@ -45,10 +44,14 @@ public static String getPersonDetails(Person person) { */ public static String getEditPersonDescriptorDetails(EditPersonDescriptor descriptor) { StringBuilder sb = new StringBuilder(); - descriptor.getName().ifPresent(name -> sb.append(PREFIX_NAME).append(name.fullName).append(" ")); - descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" ")); - descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" ")); - descriptor.getAddress().ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" ")); + descriptor.getName() + .ifPresent(name -> sb.append(PREFIX_NAME).append(name.fullName).append(" ")); + descriptor.getPhone() + .ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" ")); + descriptor.getEmail() + .ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" ")); + descriptor.getAddress() + .ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" ")); if (descriptor.getTags().isPresent()) { Set tags = descriptor.getTags().get(); if (tags.isEmpty()) { diff --git a/src/test/java/seedu/address/testutil/SerializableTestClass.java b/src/test/java/vimification/testutil/SerializableTestClass.java similarity index 98% rename from src/test/java/seedu/address/testutil/SerializableTestClass.java rename to src/test/java/vimification/testutil/SerializableTestClass.java index f5a66340489..86c6ef3252a 100644 --- a/src/test/java/seedu/address/testutil/SerializableTestClass.java +++ b/src/test/java/vimification/testutil/SerializableTestClass.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package vimification.testutil; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/src/test/java/seedu/address/testutil/TestUtil.java b/src/test/java/vimification/testutil/TestUtil.java similarity index 85% rename from src/test/java/seedu/address/testutil/TestUtil.java rename to src/test/java/vimification/testutil/TestUtil.java index 896d103eb0b..5c0db1b216d 100644 --- a/src/test/java/seedu/address/testutil/TestUtil.java +++ b/src/test/java/vimification/testutil/TestUtil.java @@ -1,13 +1,13 @@ -package seedu.address.testutil; +package vimification.testutil; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import seedu.address.commons.core.index.Index; -import seedu.address.model.Model; -import seedu.address.model.person.Person; +import vimification.commons.core.index.Index; +import vimification.model.Model; +import vimification.model.person.Person; /** * A utility class for test cases. @@ -20,8 +20,8 @@ public class TestUtil { private static final Path SANDBOX_FOLDER = Paths.get("src", "test", "data", "sandbox"); /** - * Appends {@code fileName} to the sandbox folder path and returns the resulting path. - * Creates the sandbox folder if it doesn't exist. + * Appends {@code fileName} to the sandbox folder path and returns the resulting path. Creates + * the sandbox folder if it doesn't exist. */ public static Path getFilePathInSandboxFolder(String fileName) { try { diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/vimification/testutil/TypicalIndexes.java similarity index 81% rename from src/test/java/seedu/address/testutil/TypicalIndexes.java rename to src/test/java/vimification/testutil/TypicalIndexes.java index 1e613937657..7b92325aba8 100644 --- a/src/test/java/seedu/address/testutil/TypicalIndexes.java +++ b/src/test/java/vimification/testutil/TypicalIndexes.java @@ -1,6 +1,6 @@ -package seedu.address.testutil; +package vimification.testutil; -import seedu.address.commons.core.index.Index; +import vimification.commons.core.index.Index; /** * A utility class containing a list of {@code Index} objects to be used in tests. diff --git a/src/test/java/vimification/testutil/TypicalPersons.java b/src/test/java/vimification/testutil/TypicalPersons.java new file mode 100644 index 00000000000..2e3b2e477c3 --- /dev/null +++ b/src/test/java/vimification/testutil/TypicalPersons.java @@ -0,0 +1,88 @@ +package vimification.testutil; + +import static vimification.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; +import static vimification.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; +import static vimification.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_NAME_AMY; +import static vimification.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_PHONE_AMY; +import static vimification.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; +import static vimification.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import vimification.model.AddressBook; +import vimification.model.person.Person; + +/** + * A utility class containing a list of {@code Person} objects to be used in tests. + */ +public class TypicalPersons { + + public static final Person ALICE = new PersonBuilder().withName("Alice Pauline") + .withAddress("123, Jurong West Ave 6, #08-111").withEmail("alice@example.com") + .withPhone("94351253") + .withTags("friends").build(); + public static final Person BENSON = new PersonBuilder().withName("Benson Meier") + .withAddress("311, Clementi Ave 2, #02-25") + .withEmail("johnd@example.com").withPhone("98765432") + .withTags("owesMoney", "friends").build(); + public static final Person CARL = + new PersonBuilder().withName("Carl Kurz").withPhone("95352563") + .withEmail("heinz@example.com").withAddress("wall street").build(); + public static final Person DANIEL = + new PersonBuilder().withName("Daniel Meier").withPhone("87652533") + .withEmail("cornelia@example.com").withAddress("10th street") + .withTags("friends").build(); + public static final Person ELLE = + new PersonBuilder().withName("Elle Meyer").withPhone("9482224") + .withEmail("werner@example.com").withAddress("michegan ave").build(); + public static final Person FIONA = + new PersonBuilder().withName("Fiona Kunz").withPhone("9482427") + .withEmail("lydia@example.com").withAddress("little tokyo").build(); + public static final Person GEORGE = + new PersonBuilder().withName("George Best").withPhone("9482442") + .withEmail("anna@example.com").withAddress("4th street").build(); + + // Manually added + public static final Person HOON = + new PersonBuilder().withName("Hoon Meier").withPhone("8482424") + .withEmail("stefan@example.com").withAddress("little india").build(); + public static final Person IDA = + new PersonBuilder().withName("Ida Mueller").withPhone("8482131") + .withEmail("hans@example.com").withAddress("chicago ave").build(); + + // Manually added - Person's details found in {@code CommandTestUtil} + public static final Person AMY = + new PersonBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY) + .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) + .withTags(VALID_TAG_FRIEND).build(); + public static final Person BOB = + new PersonBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) + .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB) + .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND) + .build(); + + public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER + + private TypicalPersons() {} // prevents instantiation + + /** + * Returns an {@code AddressBook} with all the typical persons. + */ + public static AddressBook getTypicalAddressBook() { + AddressBook ab = new AddressBook(); + for (Person person : getTypicalPersons()) { + ab.addPerson(person); + } + return ab; + } + + public static List getTypicalPersons() { + return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); + } +} diff --git a/src/test/java/seedu/address/ui/TestFxmlObject.java b/src/test/java/vimification/ui/TestFxmlObject.java similarity index 76% rename from src/test/java/seedu/address/ui/TestFxmlObject.java rename to src/test/java/vimification/ui/TestFxmlObject.java index 5ecd82656f2..0424b2fd274 100644 --- a/src/test/java/seedu/address/ui/TestFxmlObject.java +++ b/src/test/java/vimification/ui/TestFxmlObject.java @@ -1,10 +1,10 @@ -package seedu.address.ui; +package vimification.ui; import javafx.beans.DefaultProperty; /** - * A test object which can be constructed via an FXML file. - * Unlike other JavaFX classes, this class can be constructed without the JavaFX toolkit being initialized. + * A test object which can be constructed via an FXML file. Unlike other JavaFX classes, this class + * can be constructed without the JavaFX toolkit being initialized. */ @DefaultProperty("text") public class TestFxmlObject { diff --git a/src/test/java/seedu/address/ui/UiPartTest.java b/src/test/java/vimification/ui/UiPartTest.java similarity index 75% rename from src/test/java/seedu/address/ui/UiPartTest.java rename to src/test/java/vimification/ui/UiPartTest.java index 33d82d911b8..a4bb917d84e 100644 --- a/src/test/java/seedu/address/ui/UiPartTest.java +++ b/src/test/java/vimification/ui/UiPartTest.java @@ -1,8 +1,8 @@ -package seedu.address.ui; +package vimification.ui; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static vimification.testutil.Assert.assertThrows; import java.net.URL; import java.nio.file.Path; @@ -11,14 +11,15 @@ import org.junit.jupiter.api.io.TempDir; import javafx.fxml.FXML; -import seedu.address.MainApp; +import vimification.MainApp; public class UiPartTest { private static final String MISSING_FILE_PATH = "UiPartTest/missingFile.fxml"; private static final String INVALID_FILE_PATH = "UiPartTest/invalidFile.fxml"; private static final String VALID_FILE_PATH = "UiPartTest/validFile.fxml"; - private static final String VALID_FILE_WITH_FX_ROOT_PATH = "UiPartTest/validFileWithFxRoot.fxml"; + private static final String VALID_FILE_WITH_FX_ROOT_PATH = + "UiPartTest/validFileWithFxRoot.fxml"; private static final TestFxmlObject VALID_FILE_ROOT = new TestFxmlObject("Hello World!"); @TempDir @@ -27,21 +28,24 @@ public class UiPartTest { @Test public void constructor_nullFileUrl_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> new TestUiPart((URL) null)); - assertThrows(NullPointerException.class, () -> new TestUiPart((URL) null, new Object())); + assertThrows(NullPointerException.class, + () -> new TestUiPart((URL) null, new Object())); } @Test public void constructor_missingFileUrl_throwsAssertionError() throws Exception { URL missingFileUrl = new URL(testFolder.toUri().toURL(), MISSING_FILE_PATH); assertThrows(AssertionError.class, () -> new TestUiPart(missingFileUrl)); - assertThrows(AssertionError.class, () -> new TestUiPart(missingFileUrl, new Object())); + assertThrows(AssertionError.class, + () -> new TestUiPart(missingFileUrl, new Object())); } @Test public void constructor_invalidFileUrl_throwsAssertionError() { URL invalidFileUrl = getTestFileUrl(INVALID_FILE_PATH); assertThrows(AssertionError.class, () -> new TestUiPart(invalidFileUrl)); - assertThrows(AssertionError.class, () -> new TestUiPart(invalidFileUrl, new Object())); + assertThrows(AssertionError.class, + () -> new TestUiPart(invalidFileUrl, new Object())); } @Test @@ -60,19 +64,22 @@ public void constructor_validFileWithFxRootUrl_loadsFile() { @Test public void constructor_nullFileName_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> new TestUiPart((String) null)); - assertThrows(NullPointerException.class, () -> new TestUiPart((String) null, new Object())); + assertThrows(NullPointerException.class, + () -> new TestUiPart((String) null, new Object())); } @Test public void constructor_missingFileName_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> new TestUiPart(MISSING_FILE_PATH)); - assertThrows(NullPointerException.class, () -> new TestUiPart(MISSING_FILE_PATH, new Object())); + assertThrows(NullPointerException.class, + () -> new TestUiPart(MISSING_FILE_PATH, new Object())); } @Test public void constructor_invalidFileName_throwsAssertionError() { assertThrows(AssertionError.class, () -> new TestUiPart(INVALID_FILE_PATH)); - assertThrows(AssertionError.class, () -> new TestUiPart(INVALID_FILE_PATH, new Object())); + assertThrows(AssertionError.class, + () -> new TestUiPart(INVALID_FILE_PATH, new Object())); } private URL getTestFileUrl(String testFilePath) { @@ -83,8 +90,8 @@ private URL getTestFileUrl(String testFilePath) { } /** - * UiPart used for testing. - * It should only be used with invalid FXML files or the valid file located at {@link VALID_FILE_PATH}. + * UiPart used for testing. It should only be used with invalid FXML files or the valid file + * located at {@link VALID_FILE_PATH}. */ private static class TestUiPart extends UiPart { diff --git a/src/test/resources/view/UiPartTest/validFile.fxml b/src/test/resources/view/UiPartTest/validFile.fxml index bab836af0db..009c132f2f7 100644 --- a/src/test/resources/view/UiPartTest/validFile.fxml +++ b/src/test/resources/view/UiPartTest/validFile.fxml @@ -1,4 +1,4 @@ - + Hello World! diff --git a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml index 151e09ce926..234559f2c41 100644 --- a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml +++ b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml @@ -1,6 +1,6 @@ - Hello World! From d0a650ea15944f4b22ccb722759f6fe9f8d1040d Mon Sep 17 00:00:00 2001 From: VietAnh1010 Date: Wed, 8 Mar 2023 00:01:31 +0800 Subject: [PATCH 009/470] ParseException.java: move to common directory for parser It does not make sense to put the this exception in a separate directory. --- src/main/java/vimification/logic/Logic.java | 4 ++-- src/main/java/vimification/logic/LogicManager.java | 2 +- .../vimification/logic/parser/AddCommandParser.java | 3 +-- .../vimification/logic/parser/AddressBookParser.java | 1 - .../logic/parser/DeleteCommandParser.java | 3 +-- .../vimification/logic/parser/EditCommandParser.java | 3 +-- .../vimification/logic/parser/FindCommandParser.java | 3 +-- .../parser/{exceptions => }/ParseException.java | 2 +- src/main/java/vimification/logic/parser/Parser.java | 3 +-- .../java/vimification/logic/parser/ParserUtil.java | 3 +-- src/main/java/vimification/ui/CommandBox.java | 2 +- src/main/java/vimification/ui/MainWindow.java | 4 ++-- .../java/vimification/logic/LogicManagerTest.java | 12 ++++++------ .../logic/parser/AddressBookParserTest.java | 1 - .../logic/parser/CommandParserTestUtil.java | 1 - .../vimification/logic/parser/ParserUtilTest.java | 1 - 16 files changed, 19 insertions(+), 29 deletions(-) rename src/main/java/vimification/logic/parser/{exceptions => }/ParseException.java (88%) diff --git a/src/main/java/vimification/logic/Logic.java b/src/main/java/vimification/logic/Logic.java index 8b052e6df3f..93a9a880a42 100644 --- a/src/main/java/vimification/logic/Logic.java +++ b/src/main/java/vimification/logic/Logic.java @@ -6,7 +6,7 @@ import vimification.commons.core.GuiSettings; import vimification.logic.commands.CommandResult; import vimification.logic.commands.exceptions.CommandException; -import vimification.logic.parser.exceptions.ParseException; +import vimification.logic.parser.ParseException; import vimification.model.ReadOnlyAddressBook; import vimification.model.person.Person; @@ -16,7 +16,7 @@ public interface Logic { /** * Executes the command and returns the result. - * + * * @param commandText The command as entered by the user. * @return the result of the command execution. * @throws CommandException If an error occurs during command execution. diff --git a/src/main/java/vimification/logic/LogicManager.java b/src/main/java/vimification/logic/LogicManager.java index 2bfb31ed1db..845385122a4 100644 --- a/src/main/java/vimification/logic/LogicManager.java +++ b/src/main/java/vimification/logic/LogicManager.java @@ -11,7 +11,7 @@ import vimification.logic.commands.CommandResult; import vimification.logic.commands.exceptions.CommandException; import vimification.logic.parser.AddressBookParser; -import vimification.logic.parser.exceptions.ParseException; +import vimification.logic.parser.ParseException; import vimification.model.Model; import vimification.model.ReadOnlyAddressBook; import vimification.model.person.Person; diff --git a/src/main/java/vimification/logic/parser/AddCommandParser.java b/src/main/java/vimification/logic/parser/AddCommandParser.java index 54aaa271403..380e60794fe 100644 --- a/src/main/java/vimification/logic/parser/AddCommandParser.java +++ b/src/main/java/vimification/logic/parser/AddCommandParser.java @@ -11,7 +11,6 @@ import java.util.stream.Stream; import vimification.logic.commands.AddCommand; -import vimification.logic.parser.exceptions.ParseException; import vimification.model.person.Address; import vimification.model.person.Email; import vimification.model.person.Name; @@ -27,7 +26,7 @@ public class AddCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the AddCommand and returns an * AddCommand object for execution. - * + * * @throws ParseException if the user input does not conform the expected format */ public AddCommand parse(String args) throws ParseException { diff --git a/src/main/java/vimification/logic/parser/AddressBookParser.java b/src/main/java/vimification/logic/parser/AddressBookParser.java index 2ecb4dfb017..146742ec1d3 100644 --- a/src/main/java/vimification/logic/parser/AddressBookParser.java +++ b/src/main/java/vimification/logic/parser/AddressBookParser.java @@ -15,7 +15,6 @@ import vimification.logic.commands.FindCommand; import vimification.logic.commands.HelpCommand; import vimification.logic.commands.ListCommand; -import vimification.logic.parser.exceptions.ParseException; /** * Parses user input. diff --git a/src/main/java/vimification/logic/parser/DeleteCommandParser.java b/src/main/java/vimification/logic/parser/DeleteCommandParser.java index fce91be97e5..69298bf82ad 100644 --- a/src/main/java/vimification/logic/parser/DeleteCommandParser.java +++ b/src/main/java/vimification/logic/parser/DeleteCommandParser.java @@ -4,7 +4,6 @@ import vimification.commons.core.index.Index; import vimification.logic.commands.DeleteCommand; -import vimification.logic.parser.exceptions.ParseException; /** * Parses input arguments and creates a new DeleteCommand object @@ -14,7 +13,7 @@ public class DeleteCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the DeleteCommand and returns * a DeleteCommand object for execution. - * + * * @throws ParseException if the user input does not conform the expected format */ public DeleteCommand parse(String args) throws ParseException { diff --git a/src/main/java/vimification/logic/parser/EditCommandParser.java b/src/main/java/vimification/logic/parser/EditCommandParser.java index aad60e36afd..6a0ef4b51e1 100644 --- a/src/main/java/vimification/logic/parser/EditCommandParser.java +++ b/src/main/java/vimification/logic/parser/EditCommandParser.java @@ -16,7 +16,6 @@ import vimification.commons.core.index.Index; import vimification.logic.commands.EditCommand; import vimification.logic.commands.EditCommand.EditPersonDescriptor; -import vimification.logic.parser.exceptions.ParseException; import vimification.model.tag.Tag; /** @@ -27,7 +26,7 @@ public class EditCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the EditCommand and returns an * EditCommand object for execution. - * + * * @throws ParseException if the user input does not conform the expected format */ public EditCommand parse(String args) throws ParseException { diff --git a/src/main/java/vimification/logic/parser/FindCommandParser.java b/src/main/java/vimification/logic/parser/FindCommandParser.java index 54c250ea362..d4af96d1ad1 100644 --- a/src/main/java/vimification/logic/parser/FindCommandParser.java +++ b/src/main/java/vimification/logic/parser/FindCommandParser.java @@ -5,7 +5,6 @@ import java.util.Arrays; import vimification.logic.commands.FindCommand; -import vimification.logic.parser.exceptions.ParseException; import vimification.model.person.NameContainsKeywordsPredicate; /** @@ -16,7 +15,7 @@ public class FindCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the FindCommand and returns a * FindCommand object for execution. - * + * * @throws ParseException if the user input does not conform the expected format */ public FindCommand parse(String args) throws ParseException { diff --git a/src/main/java/vimification/logic/parser/exceptions/ParseException.java b/src/main/java/vimification/logic/parser/ParseException.java similarity index 88% rename from src/main/java/vimification/logic/parser/exceptions/ParseException.java rename to src/main/java/vimification/logic/parser/ParseException.java index 36122f69f5c..6be328688c8 100644 --- a/src/main/java/vimification/logic/parser/exceptions/ParseException.java +++ b/src/main/java/vimification/logic/parser/ParseException.java @@ -1,4 +1,4 @@ -package vimification.logic.parser.exceptions; +package vimification.logic.parser; import vimification.commons.exceptions.IllegalValueException; diff --git a/src/main/java/vimification/logic/parser/Parser.java b/src/main/java/vimification/logic/parser/Parser.java index f67dc174e19..a67be8e87d3 100644 --- a/src/main/java/vimification/logic/parser/Parser.java +++ b/src/main/java/vimification/logic/parser/Parser.java @@ -1,7 +1,6 @@ package vimification.logic.parser; import vimification.logic.commands.Command; -import vimification.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. @@ -10,7 +9,7 @@ public interface Parser { /** * Parses {@code userInput} into a command and returns it. - * + * * @throws ParseException if {@code userInput} does not conform the expected format */ T parse(String userInput) throws ParseException; diff --git a/src/main/java/vimification/logic/parser/ParserUtil.java b/src/main/java/vimification/logic/parser/ParserUtil.java index dfe1b2896cf..8c0a0eee81c 100644 --- a/src/main/java/vimification/logic/parser/ParserUtil.java +++ b/src/main/java/vimification/logic/parser/ParserUtil.java @@ -8,7 +8,6 @@ import vimification.commons.core.index.Index; import vimification.commons.util.StringUtil; -import vimification.logic.parser.exceptions.ParseException; import vimification.model.person.Address; import vimification.model.person.Email; import vimification.model.person.Name; @@ -25,7 +24,7 @@ public class ParserUtil { /** * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing * whitespaces will be trimmed. - * + * * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). */ public static Index parseIndex(String oneBasedIndex) throws ParseException { diff --git a/src/main/java/vimification/ui/CommandBox.java b/src/main/java/vimification/ui/CommandBox.java index 556133b187a..b5f84d49e45 100644 --- a/src/main/java/vimification/ui/CommandBox.java +++ b/src/main/java/vimification/ui/CommandBox.java @@ -6,7 +6,7 @@ import javafx.scene.layout.Region; import vimification.logic.commands.CommandResult; import vimification.logic.commands.exceptions.CommandException; -import vimification.logic.parser.exceptions.ParseException; +import vimification.logic.parser.ParseException; /** * The UI component that is responsible for receiving user command inputs. diff --git a/src/main/java/vimification/ui/MainWindow.java b/src/main/java/vimification/ui/MainWindow.java index 2fa89b507d8..ec0fbae1cc5 100644 --- a/src/main/java/vimification/ui/MainWindow.java +++ b/src/main/java/vimification/ui/MainWindow.java @@ -15,7 +15,7 @@ import vimification.logic.Logic; import vimification.logic.commands.CommandResult; import vimification.logic.commands.exceptions.CommandException; -import vimification.logic.parser.exceptions.ParseException; +import vimification.logic.parser.ParseException; /** * The Main Window. Provides the basic application layout containing a menu bar and space where @@ -78,7 +78,7 @@ private void setAccelerators() { /** * Sets the accelerator of a MenuItem. - * + * * @param keyCombination the KeyCombination value of the accelerator */ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { diff --git a/src/test/java/vimification/logic/LogicManagerTest.java b/src/test/java/vimification/logic/LogicManagerTest.java index 2ebc7946a5c..048dbd496f8 100644 --- a/src/test/java/vimification/logic/LogicManagerTest.java +++ b/src/test/java/vimification/logic/LogicManagerTest.java @@ -21,7 +21,7 @@ import vimification.logic.commands.CommandResult; import vimification.logic.commands.ListCommand; import vimification.logic.commands.exceptions.CommandException; -import vimification.logic.parser.exceptions.ParseException; +import vimification.logic.parser.ParseException; import vimification.model.Model; import vimification.model.ModelManager; import vimification.model.ReadOnlyAddressBook; @@ -101,7 +101,7 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException * Executes the command and confirms that - no exceptions are thrown
* - the feedback message is equal to {@code expectedMessage}
* - the internal model manager state is the same as that in {@code expectedModel}
- * + * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertCommandSuccess(String inputCommand, String expectedMessage, @@ -114,7 +114,7 @@ private void assertCommandSuccess(String inputCommand, String expectedMessage, /** * Executes the command, confirms that a ParseException is thrown and that the result message is * correct. - * + * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertParseException(String inputCommand, String expectedMessage) { @@ -124,7 +124,7 @@ private void assertParseException(String inputCommand, String expectedMessage) { /** * Executes the command, confirms that a CommandException is thrown and that the result message * is correct. - * + * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertCommandException(String inputCommand, String expectedMessage) { @@ -134,7 +134,7 @@ private void assertCommandException(String inputCommand, String expectedMessage) /** * Executes the command, confirms that the exception is thrown and that the result message is * correct. - * + * * @see #assertCommandFailure(String, Class, String, Model) */ private void assertCommandFailure(String inputCommand, @@ -148,7 +148,7 @@ private void assertCommandFailure(String inputCommand, * Executes the command and confirms that - the {@code expectedException} is thrown
* - the resulting error message is equal to {@code expectedMessage}
* - the internal model manager state is the same as that in {@code expectedModel}
- * + * * @see #assertCommandSuccess(String, String, Model) */ private void assertCommandFailure(String inputCommand, diff --git a/src/test/java/vimification/logic/parser/AddressBookParserTest.java b/src/test/java/vimification/logic/parser/AddressBookParserTest.java index 13f71ac5384..596e4cb5270 100644 --- a/src/test/java/vimification/logic/parser/AddressBookParserTest.java +++ b/src/test/java/vimification/logic/parser/AddressBookParserTest.java @@ -22,7 +22,6 @@ import vimification.logic.commands.FindCommand; import vimification.logic.commands.HelpCommand; import vimification.logic.commands.ListCommand; -import vimification.logic.parser.exceptions.ParseException; import vimification.model.person.NameContainsKeywordsPredicate; import vimification.model.person.Person; import vimification.testutil.EditPersonDescriptorBuilder; diff --git a/src/test/java/vimification/logic/parser/CommandParserTestUtil.java b/src/test/java/vimification/logic/parser/CommandParserTestUtil.java index 7ba1674ac71..0c04a8998a0 100644 --- a/src/test/java/vimification/logic/parser/CommandParserTestUtil.java +++ b/src/test/java/vimification/logic/parser/CommandParserTestUtil.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import vimification.logic.commands.Command; -import vimification.logic.parser.exceptions.ParseException; /** * Contains helper methods for testing command parsers. diff --git a/src/test/java/vimification/logic/parser/ParserUtilTest.java b/src/test/java/vimification/logic/parser/ParserUtilTest.java index 589e1bb3f4f..2db8d8fe70e 100644 --- a/src/test/java/vimification/logic/parser/ParserUtilTest.java +++ b/src/test/java/vimification/logic/parser/ParserUtilTest.java @@ -13,7 +13,6 @@ import org.junit.jupiter.api.Test; -import vimification.logic.parser.exceptions.ParseException; import vimification.model.person.Address; import vimification.model.person.Email; import vimification.model.person.Name; From 9306330dc6af26ca86237b895a8f5b2368faa728 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 8 Mar 2023 13:58:16 +0800 Subject: [PATCH 010/470] Implement Remark Command --- .../seedu/address/logic/commands/RemarkCommand.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/address/logic/commands/RemarkCommand.java b/src/main/java/seedu/address/logic/commands/RemarkCommand.java index ad3d2cf931e..b90246f7d04 100644 --- a/src/main/java/seedu/address/logic/commands/RemarkCommand.java +++ b/src/main/java/seedu/address/logic/commands/RemarkCommand.java @@ -1,13 +1,18 @@ package seedu.address.logic.commands; +import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; public class RemarkCommand extends Command { - public static String COMMAND_WORD = "remark"; + public static final String COMMAND_WORD = "remark"; + + public static final String MESSAGE_SUCCESS = "Created a remark"; @Override - public CommandResult execute(Model model) { - return new CommandResult("Hello from remark"); + public CommandResult execute(Model model) throws CommandException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'execute'"); } + } From 17223874454802c9b3b3844e321d85db5e6e2877 Mon Sep 17 00:00:00 2001 From: Ethan Date: Sun, 12 Mar 2023 15:31:39 +0800 Subject: [PATCH 011/470] Update Ethan portfolio --- docs/AboutUs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index d039a5b28dd..a2de61f9b24 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -29,7 +29,7 @@ You can reach us at the email `what email should we put here?` - Role: Team Lead - Responsibilities: UI -### Ethan Chen Ee Shuen +## Ethan Chen Ee Shuen From cb55fd9c18b84fcfebce7325679dd33cd0ccf44a Mon Sep 17 00:00:00 2001 From: Ethan Date: Sun, 12 Mar 2023 15:40:45 +0800 Subject: [PATCH 012/470] Modify Ethan portfolio page --- docs/team/bipbipboopboop.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/team/bipbipboopboop.md b/docs/team/bipbipboopboop.md index e69de29bb2d..539af81b2ff 100644 --- a/docs/team/bipbipboopboop.md +++ b/docs/team/bipbipboopboop.md @@ -0,0 +1,8 @@ +--- +layout: page +title: Ethan's Project Portfolio Page +--- + +# Project: Vimification + +More details will be added soon... From 71d3ad7b6b7763d6e639ad294d219fa608919bd4 Mon Sep 17 00:00:00 2001 From: liujiayue314 Date: Sun, 12 Mar 2023 15:56:59 +0800 Subject: [PATCH 013/470] Update profile photo --- docs/images/liujiayue314.png | Bin 0 -> 268704 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/liujiayue314.png diff --git a/docs/images/liujiayue314.png b/docs/images/liujiayue314.png new file mode 100644 index 0000000000000000000000000000000000000000..008811d41b9ffb4c0f61336527db82ec310c6187 GIT binary patch literal 268704 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%0Dk87HWwi6nn#;0`=LfD8fiqZ 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¥?;dSbBbTq?VC|bL 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 Date: Sun, 12 Mar 2023 18:41:33 +0800 Subject: [PATCH 014/470] Add portfolio page --- docs/team/liujiayue314.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 docs/team/liujiayue314.md diff --git a/docs/team/liujiayue314.md b/docs/team/liujiayue314.md new file mode 100644 index 00000000000..ed085668525 --- /dev/null +++ b/docs/team/liujiayue314.md @@ -0,0 +1,8 @@ +--- +layout: page +title: Liu JiaYue's Project Portfolio Page +--- + +# Project: Vimification + +More details will be added soon... From 1bb9cc53baa8f22e7a602b9f65ffe42b8db58f2b Mon Sep 17 00:00:00 2001 From: liujiayue314 <97432782+liujiayue314@users.noreply.github.com> Date: Sun, 12 Mar 2023 19:12:21 +0800 Subject: [PATCH 015/470] Update AboutUs --- docs/AboutUs.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index a2de61f9b24..c52e59e2b5a 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -19,15 +19,15 @@ You can reach us at the email `what email should we put here?` - Role: Team lead, Integration, Java expert - Responsibilities: Parser, Model -## Jane Doe +## Liu JiaYue - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/liujiayue314)] +[[portfolio](team/liujiayue314.md)] -- Role: Team Lead -- Responsibilities: UI +- Role: Developer +- Responsibilities: Storage ## Ethan Chen Ee Shuen From f25aab9d33b675cc4c93d9e11b551b1b5245d747 Mon Sep 17 00:00:00 2001 From: liujiayue314 <97432782+liujiayue314@users.noreply.github.com> Date: Mon, 13 Mar 2023 02:47:23 +0800 Subject: [PATCH 016/470] Update README.md --- README.md | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 13f5c77403f..7077cedf1c5 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,9 @@ ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. + +* Vimification is a task planner application, optimised for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). The target audience are users who prefers to use vim commands and minimal mouse interactions to increase effectiveness and speed. +* It is named `Vimification` because it was designed for users that are proficient in vim. +* For the detailed documentation of this project, see the **[Vimification Product Website](https://ay2223s2-cs2103t-t15-3.github.io/tp/)**. * This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). From eada1f7acdaeb0107459f158a3d85e7b695ca0af Mon Sep 17 00:00:00 2001 From: DerenC Date: Mon, 13 Mar 2023 16:43:03 +0800 Subject: [PATCH 017/470] Add Deren's portfolio page --- docs/team/DerenC.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 docs/team/DerenC.md diff --git a/docs/team/DerenC.md b/docs/team/DerenC.md new file mode 100644 index 00000000000..36c58f3ef03 --- /dev/null +++ b/docs/team/DerenC.md @@ -0,0 +1,8 @@ +--- +layout: page +title: Deren's Project Portfolio Page +--- + +# Project: Vimification + +More details will be added soon... \ No newline at end of file From c35abdc1aae88dc2e855027094e816e3b033b302 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 13 Mar 2023 17:05:26 +0800 Subject: [PATCH 018/470] Add some basic UI components and create Main.fxml for MainUi --- .vscode/settings.json | 12 +- .../java/vimification/VimificationApp.java | 191 ++++++++++++++++++ .../vimification/model/task/Description.java | 16 ++ .../java/vimification/model/task/Task.java | 45 +++++ .../java/vimification/model/task/Title.java | 58 ++++++ .../java/vimification/taskui/MainWindow.java | 114 +++++++++++ .../java/vimification/taskui/TaskCard.java | 83 ++++++++ .../vimification/taskui/TaskListPanel.java | 48 +++++ src/main/java/vimification/taskui/Ui.java | 13 ++ .../java/vimification/taskui/UiManager.java | 90 +++++++++ src/main/java/vimification/taskui/UiPart.java | 92 +++++++++ src/main/resources/view/Main.fxml | 22 ++ src/main/resources/view/TaskListCard.fxml | 34 ++++ src/main/resources/view/TaskListPanel.fxml | 8 + 14 files changed, 817 insertions(+), 9 deletions(-) create mode 100644 src/main/java/vimification/VimificationApp.java create mode 100644 src/main/java/vimification/model/task/Description.java create mode 100644 src/main/java/vimification/model/task/Task.java create mode 100644 src/main/java/vimification/model/task/Title.java create mode 100644 src/main/java/vimification/taskui/MainWindow.java create mode 100644 src/main/java/vimification/taskui/TaskCard.java create mode 100644 src/main/java/vimification/taskui/TaskListPanel.java create mode 100644 src/main/java/vimification/taskui/Ui.java create mode 100644 src/main/java/vimification/taskui/UiManager.java create mode 100644 src/main/java/vimification/taskui/UiPart.java create mode 100644 src/main/resources/view/Main.fxml create mode 100644 src/main/resources/view/TaskListCard.fxml create mode 100644 src/main/resources/view/TaskListPanel.fxml diff --git a/.vscode/settings.json b/.vscode/settings.json index c9eda2d29a2..6252dca7b87 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,12 +1,5 @@ { - "java.completion.importOrder": [ - "#", - "java", - "javax", - "org", - "com", - "" - ], + "java.completion.importOrder": ["#", "java", "javax", "org", "com", ""], "java.settings.url": ".vscode/vscode-java.prefs", "java.format.settings.url": ".vscode/eclipse-java-google-style.xml", "java.project.sourcePaths": ["src/main/java", "src/test/java", ""], @@ -21,5 +14,6 @@ "files.trimTrailingWhitespace": true, "java.inlayHints.parameterNames.enabled": "none", /* enable if you want to see VSCode suggestions */ - "css.validate": false + "css.validate": false, + "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable" } diff --git a/src/main/java/vimification/VimificationApp.java b/src/main/java/vimification/VimificationApp.java new file mode 100644 index 00000000000..2b506c6adb0 --- /dev/null +++ b/src/main/java/vimification/VimificationApp.java @@ -0,0 +1,191 @@ +package vimification; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import javafx.application.Application; +import javafx.stage.Stage; +import vimification.commons.core.Config; +import vimification.commons.core.LogsCenter; +import vimification.commons.core.Version; +import vimification.commons.exceptions.DataConversionException; +import vimification.commons.util.ConfigUtil; +import vimification.commons.util.StringUtil; +import vimification.logic.Logic; +import vimification.logic.LogicManager; +import vimification.model.AddressBook; +import vimification.model.Model; +import vimification.model.ModelManager; +import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyUserPrefs; +import vimification.model.UserPrefs; +import vimification.model.util.SampleDataUtil; +import vimification.storage.AddressBookStorage; +import vimification.storage.JsonAddressBookStorage; +import vimification.storage.JsonUserPrefsStorage; +import vimification.storage.Storage; +import vimification.storage.StorageManager; +import vimification.storage.UserPrefsStorage; +import vimification.ui.Ui; +import vimification.ui.UiManager; + +/** + * Runs the application. + */ +public class VimificationApp extends Application { + + public static final Version VERSION = new Version(0, 2, 0, true); + + private static final Logger logger = LogsCenter.getLogger(MainApp.class); + + protected Ui ui; + protected Logic logic; + protected Storage storage; + protected Model model; + protected Config config; + + @Override + public void init() throws Exception { + logger.info( + "=============================[ Initializing AddressBook ]==========================="); + super.init(); + + AppParameters appParameters = AppParameters.parse(getParameters()); + config = initConfig(appParameters.getConfigPath()); + + UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); + UserPrefs userPrefs = initPrefs(userPrefsStorage); + AddressBookStorage addressBookStorage = + new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); + storage = new StorageManager(addressBookStorage, userPrefsStorage); + + initLogging(config); + + model = initModelManager(storage, userPrefs); + + logic = new LogicManager(model, storage); + + ui = new UiManager(logic); + } + + /** + * Returns a {@code ModelManager} with the data from {@code storage}'s address book and + * {@code userPrefs}.
+ * The data from the sample address book will be used instead if {@code storage}'s address book + * is not found, or an empty address book will be used instead if errors occur when reading + * {@code storage}'s address book. + */ + private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { + Optional addressBookOptional; + ReadOnlyAddressBook initialData; + try { + addressBookOptional = storage.readAddressBook(); + if (!addressBookOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample AddressBook"); + } + initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + } catch (DataConversionException e) { + logger.warning( + "Data file not in the correct format. Will be starting with an empty AddressBook"); + initialData = new AddressBook(); + } catch (IOException e) { + logger.warning( + "Problem while reading from the file. Will be starting with an empty AddressBook"); + initialData = new AddressBook(); + } + + return new ModelManager(initialData, userPrefs); + } + + private void initLogging(Config config) { + LogsCenter.init(config); + } + + /** + * Returns a {@code Config} using the file at {@code configFilePath}.
+ * The default file path {@code Config#DEFAULT_CONFIG_FILE} will be used instead if + * {@code configFilePath} is null. + */ + protected Config initConfig(Path configFilePath) { + Config initializedConfig; + Path configFilePathUsed; + + configFilePathUsed = Config.DEFAULT_CONFIG_FILE; + + if (configFilePath != null) { + logger.info("Custom Config file specified " + configFilePath); + configFilePathUsed = configFilePath; + } + + logger.info("Using config file : " + configFilePathUsed); + + try { + Optional configOptional = ConfigUtil.readConfig(configFilePathUsed); + initializedConfig = configOptional.orElse(new Config()); + } catch (DataConversionException e) { + logger.warning( + "Config file at " + configFilePathUsed + " is not in the correct format. " + + "Using default config properties"); + initializedConfig = new Config(); + } + + // Update config file in case it was missing to begin with or there are new/unused fields + try { + ConfigUtil.saveConfig(initializedConfig, configFilePathUsed); + } catch (IOException e) { + logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); + } + return initializedConfig; + } + + /** + * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs file path, or a + * new {@code UserPrefs} with default configuration if errors occur when reading from the file. + */ + protected UserPrefs initPrefs(UserPrefsStorage storage) { + Path prefsFilePath = storage.getUserPrefsFilePath(); + logger.info("Using prefs file : " + prefsFilePath); + + UserPrefs initializedPrefs; + try { + Optional prefsOptional = storage.readUserPrefs(); + initializedPrefs = prefsOptional.orElse(new UserPrefs()); + } catch (DataConversionException e) { + logger.warning("UserPrefs file at " + prefsFilePath + " is not in the correct format. " + + "Using default user prefs"); + initializedPrefs = new UserPrefs(); + } catch (IOException e) { + logger.warning( + "Problem while reading from the file. Will be starting with an empty AddressBook"); + initializedPrefs = new UserPrefs(); + } + + // Update prefs file in case it was missing to begin with or there are new/unused fields + try { + storage.saveUserPrefs(initializedPrefs); + } catch (IOException e) { + logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); + } + + return initializedPrefs; + } + + @Override + public void start(Stage primaryStage) { + logger.info("Starting AddressBook " + MainApp.VERSION); + ui.start(primaryStage); + } + + @Override + public void stop() { + logger.info( + "============================ [ Stopping Address Book ] ============================="); + try { + storage.saveUserPrefs(model.getUserPrefs()); + } catch (IOException e) { + logger.severe("Failed to save preferences " + StringUtil.getDetails(e)); + } + } +} diff --git a/src/main/java/vimification/model/task/Description.java b/src/main/java/vimification/model/task/Description.java new file mode 100644 index 00000000000..911cd7caf59 --- /dev/null +++ b/src/main/java/vimification/model/task/Description.java @@ -0,0 +1,16 @@ +package vimification.model.task; + +public class Description { + private final String text; + + + public Description(String text) { + this.text = text; + } + + public String getText() { + return this.text; + } + + +} diff --git a/src/main/java/vimification/model/task/Task.java b/src/main/java/vimification/model/task/Task.java new file mode 100644 index 00000000000..7e643c0e9c9 --- /dev/null +++ b/src/main/java/vimification/model/task/Task.java @@ -0,0 +1,45 @@ +package vimification.model.task; + +import java.util.HashSet; +import java.util.Set; + +public class Task { + private final Title title; + private final Description description; + private String deadline; + + private Set tags = new HashSet<>(); + + /** + * Every field must be present and not null. + */ + public Task(Title title, Description description) { + // requireAllNonNull(name, phone, email, address, tags); + this.title = title; + this.description = description; + // this.deadline = deadline; + // this.tags.addAll(tags); + } + + + public Title getTitle() { + return title; + } + + + public Description getDescription() { + return description; + } + + + public String getDeadline() { + return deadline; + } + + + public Set getTags() { + return tags; + } + + +} diff --git a/src/main/java/vimification/model/task/Title.java b/src/main/java/vimification/model/task/Title.java new file mode 100644 index 00000000000..494fc128ef9 --- /dev/null +++ b/src/main/java/vimification/model/task/Title.java @@ -0,0 +1,58 @@ +package vimification.model.task; + +import static java.util.Objects.requireNonNull; +import static vimification.commons.util.AppUtil.checkArgument; + +/** + * Represents a Task's title in Vimification. + * + */ +public class Title { + + public static final String MESSAGE_CONSTRAINTS = + "Tasks should only contain alphanumeric characters and spaces, and it should not be blank"; + + /* + * The first character of the address must not be a whitespace, otherwise " " (a blank string) + * becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + + public final String fullTitle; + + /** + * Constructs a {@code Name}. + * + * @param name A valid name. + */ + public Title(String name) { + requireNonNull(name); + checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); + fullTitle = name; + } + + /** + * Returns true if a given string is a valid name. + */ + public static boolean isValidName(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return fullTitle; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Title // instanceof handles nulls + && fullTitle.equals(((Title) other).fullTitle)); // state check + } + + @Override + public int hashCode() { + return fullTitle.hashCode(); + } + +} diff --git a/src/main/java/vimification/taskui/MainWindow.java b/src/main/java/vimification/taskui/MainWindow.java new file mode 100644 index 00000000000..c2b704f9658 --- /dev/null +++ b/src/main/java/vimification/taskui/MainWindow.java @@ -0,0 +1,114 @@ +package vimification.taskui; + + +import javafx.fxml.FXML; +// import javafx.scene.control.MenuItem; +// import javafx.scene.control.TextInputControl; +// import javafx.scene.input.KeyCombination; +// import javafx.scene.input.KeyEvent; +// import javafx.scene.layout.StackPane; +import javafx.stage.Stage; +import vimification.logic.Logic; + + +/** + * The Main Window. Provides the basic application layout containing a menu bar and space where + * other JavaFX elements can be placed. + */ +public class MainWindow extends UiPart { + + private static final String FXML = "MainWindow.fxml"; + + private Stage primaryStage; + private Logic logic; + + // Independent Ui parts residing in this Ui container + private TaskListPanel taskListPanel; + // private ResultDisplay resultDisplay; + // private HelpWindow helpWindow; + + /** + * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. + */ + public MainWindow(Stage primaryStage, Logic logic) { + super(FXML, primaryStage); + + // Set dependencies + this.primaryStage = primaryStage; + this.logic = logic; + + // Configure the UI + // setWindowDefaultSize(logic.getGuiSettings()); + } + + public Stage getPrimaryStage() { + return primaryStage; + } + + /** + * Fills up all the placeholders of this window. + */ + void fillInnerParts() { + // taskListPanel = new TaskListPanel(); + + // personListPanelPlaceholder.getChildren().add(taskListPanel.getRoot()); + + // resultDisplay = new ResultDisplay(); + // resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); + + // StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + // statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); + + // CommandBox commandBox = new CommandBox(this::executeCommand); + // commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); + } + + /** + * Closes the application. + */ + @FXML + private void handleExit() { + // GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), + // primaryStage.getHeight(), + // (int) primaryStage.getX(), (int) primaryStage.getY()); + // logic.setGuiSettings(guiSettings); + // helpWindow.hide(); + // primaryStage.hide(); + } + + public TaskListPanel getTaskListPanel() { + return taskListPanel; + } + + /** + * Executes the command and returns the result. + * + * @see vimification.logic.Logic#execute(String) + */ + private void executeCommand(String commandText) { + // throws CommandException, ParseException { + // try { + // CommandResult commandResult = logic.execute(commandText); + // logger.info("Result: " + commandResult.getFeedbackToUser()); + // resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); + + // if (commandResult.isShowHelp()) { + // handleHelp(); + // } + + // if (commandResult.isExit()) { + // handleExit(); + // } + + // return commandResult; + // } catch (CommandException | ParseException e) { + // logger.info("Invalid command: " + commandText); + // resultDisplay.setFeedbackToUser(e.getMessage()); + // throw e; + // } + } + + void show() { + primaryStage.show(); + } +} diff --git a/src/main/java/vimification/taskui/TaskCard.java b/src/main/java/vimification/taskui/TaskCard.java new file mode 100644 index 00000000000..1f697fa3973 --- /dev/null +++ b/src/main/java/vimification/taskui/TaskCard.java @@ -0,0 +1,83 @@ +package vimification.taskui; + +import java.util.Comparator; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import vimification.model.task.Description; +import vimification.model.task.Task; +import vimification.model.task.Title; +import vimification.ui.UiPart; + +/** + * An UI component that displays information of a {@code Person}. + */ +public class TaskCard extends UiPart { + + private static final String FXML = "TaskListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. As + * a consequence, UI elements' variable names cannot be set to such keywords or an exception + * will be thrown by JavaFX during runtime. + * + * @see
The issue on + * AddressBook level 4 + */ + + public final Task task = new Task(new Title("hi"), new Description("hi")); + + @FXML + private HBox cardPane; + @FXML + private Label title; + @FXML + private Label id; + // @FXML + // private Label phone; + // @FXML + // private Label address; + // @FXML + // private Label email; + @FXML + private FlowPane tags; + + /** + * Creates a {@code PersonCode} with the given {@code Person} and index to display. + */ + public TaskCard(Task task, int displayedIndex) { + super(FXML); + // this.task = task; + id.setText(displayedIndex + ". "); + title.setText(task.getTitle().fullTitle); + + // phone.setText(task.getPhone().value); + // address.setText(task.getAddress().value); + // email.setText(task.getEmail().value); + + // task.getTags().stream() + // .sorted(Comparator.comparing(tag -> tag.tagName)) + // .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TaskCard)) { + return false; + } + + // state check + TaskCard card = (TaskCard) other; + return id.getText().equals(card.id.getText()) + && task.equals(card.task); + } +} diff --git a/src/main/java/vimification/taskui/TaskListPanel.java b/src/main/java/vimification/taskui/TaskListPanel.java new file mode 100644 index 00000000000..7c49aac74b3 --- /dev/null +++ b/src/main/java/vimification/taskui/TaskListPanel.java @@ -0,0 +1,48 @@ +package vimification.taskui; + + +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import vimification.model.task.Task; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; + +/** + * Panel containing the list of tasks. + */ +public class TaskListPanel extends UiPart { + private static final String FXML = "TaskListPanel.fxml"; + + @FXML + private ListView taskListView; + + + /** + * Creates a {@code PersonListPanel} with the given {@code ObservableList}. + */ + public TaskListPanel(ObservableList personList) { + super(FXML); + taskListView.setItems(personList); + taskListView.setCellFactory(listView -> new TaskListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Person} using a + * {@code PersonCard}. + */ + class TaskListViewCell extends ListCell { + @Override + protected void updateItem(Task person, boolean empty) { + super.updateItem(person, empty); + + if (empty || person == null) { + setGraphic(null); + setText(null); + } + // else { + // setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); + // } + } + } +} diff --git a/src/main/java/vimification/taskui/Ui.java b/src/main/java/vimification/taskui/Ui.java new file mode 100644 index 00000000000..66fe1c2a405 --- /dev/null +++ b/src/main/java/vimification/taskui/Ui.java @@ -0,0 +1,13 @@ +package vimification.taskui; + +import javafx.stage.Stage; + +/** + * API of UI component + */ +public interface Ui { + + /** Starts the UI (and the App). */ + void start(Stage primaryStage); + +} diff --git a/src/main/java/vimification/taskui/UiManager.java b/src/main/java/vimification/taskui/UiManager.java new file mode 100644 index 00000000000..3bd8c53f3d6 --- /dev/null +++ b/src/main/java/vimification/taskui/UiManager.java @@ -0,0 +1,90 @@ +package vimification.taskui; + +import java.util.logging.Logger; + +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.image.Image; +import javafx.stage.Stage; +import vimification.MainApp; +import vimification.commons.core.LogsCenter; +import vimification.commons.util.StringUtil; +import vimification.logic.Logic; + +/** + * The manager of the UI component. + */ +public class UiManager implements Ui { + + public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane"; + + private static final Logger logger = LogsCenter.getLogger(UiManager.class); + private static final String ICON_APPLICATION = "/images/address_book_32.png"; + + private Logic logic; + private MainWindow mainWindow; + + /** + * Creates a {@code UiManager} with the given {@code Logic}. + */ + public UiManager(Logic logic) { + this.logic = logic; + } + + @Override + public void start(Stage primaryStage) { + logger.info("Starting UI..."); + + // Set the application icon. + primaryStage.getIcons().add(getImage(ICON_APPLICATION)); + + try { + mainWindow = new MainWindow(primaryStage, logic); + mainWindow.show(); // This should be called before creating other UI parts + mainWindow.fillInnerParts(); + + } catch (Throwable e) { + logger.severe(StringUtil.getDetails(e)); + showFatalErrorDialogAndShutdown("Fatal error during initializing", e); + } + } + + private Image getImage(String imagePath) { + return new Image(MainApp.class.getResourceAsStream(imagePath)); + } + + void showAlertDialogAndWait(Alert.AlertType type, String title, String headerText, + String contentText) { + showAlertDialogAndWait(mainWindow.getPrimaryStage(), type, title, headerText, contentText); + } + + /** + * Shows an alert dialog on {@code owner} with the given parameters. This method only returns + * after the user has closed the alert dialog. + */ + private static void showAlertDialogAndWait(Stage owner, AlertType type, String title, + String headerText, + String contentText) { + final Alert alert = new Alert(type); + alert.getDialogPane().getStylesheets().add("view/DarkTheme.css"); + alert.initOwner(owner); + alert.setTitle(title); + alert.setHeaderText(headerText); + alert.setContentText(contentText); + alert.getDialogPane().setId(ALERT_DIALOG_PANE_FIELD_ID); + alert.showAndWait(); + } + + /** + * Shows an error alert dialog with {@code title} and error message, {@code e}, and exits the + * application after the user has closed the alert dialog. + */ + private void showFatalErrorDialogAndShutdown(String title, Throwable e) { + logger.severe(title + " " + e.getMessage() + StringUtil.getDetails(e)); + showAlertDialogAndWait(Alert.AlertType.ERROR, title, e.getMessage(), e.toString()); + Platform.exit(); + System.exit(1); + } + +} diff --git a/src/main/java/vimification/taskui/UiPart.java b/src/main/java/vimification/taskui/UiPart.java new file mode 100644 index 00000000000..bf2f0e94da7 --- /dev/null +++ b/src/main/java/vimification/taskui/UiPart.java @@ -0,0 +1,92 @@ +package vimification.taskui; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.net.URL; + +import javafx.fxml.FXMLLoader; +import vimification.MainApp; + +/** + * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. It + * contains a scene graph with a root node of type {@code T}. + */ +public abstract class UiPart { + + /** Resource folder where FXML files are stored. */ + public static final String FXML_FILE_FOLDER = "/view/"; + + private final FXMLLoader fxmlLoader = new FXMLLoader(); + + /** + * Constructs a UiPart with the specified FXML file URL. The FXML file must not specify the + * {@code fx:controller} attribute. + */ + public UiPart(URL fxmlFileUrl) { + loadFxmlFile(fxmlFileUrl, null); + } + + /** + * Constructs a UiPart using the specified FXML file within {@link #FXML_FILE_FOLDER}. + * + * @see #UiPart(URL) + */ + public UiPart(String fxmlFileName) { + this(getFxmlFileUrl(fxmlFileName)); + } + + /** + * Constructs a UiPart with the specified FXML file URL and root object. The FXML file must not + * specify the {@code fx:controller} attribute. + */ + public UiPart(URL fxmlFileUrl, T root) { + loadFxmlFile(fxmlFileUrl, root); + } + + /** + * Constructs a UiPart with the specified FXML file within {@link #FXML_FILE_FOLDER} and root + * object. + * + * @see #UiPart(URL, T) + */ + public UiPart(String fxmlFileName, T root) { + this(getFxmlFileUrl(fxmlFileName), root); + } + + /** + * Returns the root object of the scene graph of this UiPart. + */ + public T getRoot() { + return fxmlLoader.getRoot(); + } + + /** + * Loads the object hierarchy from a FXML document. + * + * @param location Location of the FXML document. + * @param root Specifies the root of the object hierarchy. + */ + private void loadFxmlFile(URL location, T root) { + requireNonNull(location); + fxmlLoader.setLocation(location); + fxmlLoader.setController(this); + fxmlLoader.setRoot(root); + try { + fxmlLoader.load(); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + /** + * Returns the FXML file URL for the specified FXML file name within {@link #FXML_FILE_FOLDER}. + */ + private static URL getFxmlFileUrl(String fxmlFileName) { + requireNonNull(fxmlFileName); + String fxmlFileNameWithFolder = FXML_FILE_FOLDER + fxmlFileName; + URL fxmlFileUrl = MainApp.class.getResource(fxmlFileNameWithFolder); + return requireNonNull(fxmlFileUrl); + } + +} diff --git a/src/main/resources/view/Main.fxml b/src/main/resources/view/Main.fxml new file mode 100644 index 00000000000..4f49f56379b --- /dev/null +++ b/src/main/resources/view/Main.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/src/main/resources/view/TaskListCard.fxml b/src/main/resources/view/TaskListCard.fxml new file mode 100644 index 00000000000..d14faed8c97 --- /dev/null +++ b/src/main/resources/view/TaskListCard.fxml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/TaskListPanel.fxml b/src/main/resources/view/TaskListPanel.fxml new file mode 100644 index 00000000000..9c4917370a7 --- /dev/null +++ b/src/main/resources/view/TaskListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + From 53a61560cb22352f4cd57f8fbb3d32189469d14d Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 13 Mar 2023 23:16:06 +0800 Subject: [PATCH 019/470] Test custom controller --- src/main/java/vimification/Gui.java | 24 +++ src/main/java/vimification/Main.java | 4 +- .../java/vimification/VimificationApp.java | 191 ------------------ .../logic/commands/ListCommand.java | 1 - .../logic/commands/RemarkCommand.java | 6 +- .../java/vimification/taskui/MainWindow.java | 140 ++++++------- .../java/vimification/taskui/UiManager.java | 90 --------- src/main/resources/view/Main.fxml | 4 +- 8 files changed, 92 insertions(+), 368 deletions(-) create mode 100644 src/main/java/vimification/Gui.java delete mode 100644 src/main/java/vimification/VimificationApp.java delete mode 100644 src/main/java/vimification/taskui/UiManager.java diff --git a/src/main/java/vimification/Gui.java b/src/main/java/vimification/Gui.java new file mode 100644 index 00000000000..626e1352e20 --- /dev/null +++ b/src/main/java/vimification/Gui.java @@ -0,0 +1,24 @@ +package vimification; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.stage.Stage; +import vimification.taskui.MainWindow; + +public class Gui extends Application { + + + @Override + public void start(Stage primaryStage) throws Exception { + // Create control + MainWindow mainWindow = new MainWindow(); + + // Set window + // configureStage(primaryStage); + Scene scene = new Scene(mainWindow); + primaryStage.setScene(scene); // Setting the stage to show our screen + primaryStage.show(); // Render the stage. + + } + +} diff --git a/src/main/java/vimification/Main.java b/src/main/java/vimification/Main.java index d75dd744c91..fbd2424e156 100644 --- a/src/main/java/vimification/Main.java +++ b/src/main/java/vimification/Main.java @@ -12,13 +12,13 @@ * * The reason is that MainApp extends Application. In that case, the LauncherHelper will check for * the javafx.graphics module to be present as a named module. We don't use JavaFX via the module - * system so it can't find the javafx.graphics module, and so the launch is aborted. + * system so it can't find the javafx.graphics `module, and so the launch is aborted. * * By having a separate main class (Main) that doesn't extend Application to be the entry point of * the application, we avoid this issue. */ public class Main { public static void main(String[] args) { - Application.launch(MainUi.class, args); + Application.launch(Gui.class, args); } } diff --git a/src/main/java/vimification/VimificationApp.java b/src/main/java/vimification/VimificationApp.java deleted file mode 100644 index 2b506c6adb0..00000000000 --- a/src/main/java/vimification/VimificationApp.java +++ /dev/null @@ -1,191 +0,0 @@ -package vimification; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; -import java.util.logging.Logger; - -import javafx.application.Application; -import javafx.stage.Stage; -import vimification.commons.core.Config; -import vimification.commons.core.LogsCenter; -import vimification.commons.core.Version; -import vimification.commons.exceptions.DataConversionException; -import vimification.commons.util.ConfigUtil; -import vimification.commons.util.StringUtil; -import vimification.logic.Logic; -import vimification.logic.LogicManager; -import vimification.model.AddressBook; -import vimification.model.Model; -import vimification.model.ModelManager; -import vimification.model.ReadOnlyAddressBook; -import vimification.model.ReadOnlyUserPrefs; -import vimification.model.UserPrefs; -import vimification.model.util.SampleDataUtil; -import vimification.storage.AddressBookStorage; -import vimification.storage.JsonAddressBookStorage; -import vimification.storage.JsonUserPrefsStorage; -import vimification.storage.Storage; -import vimification.storage.StorageManager; -import vimification.storage.UserPrefsStorage; -import vimification.ui.Ui; -import vimification.ui.UiManager; - -/** - * Runs the application. - */ -public class VimificationApp extends Application { - - public static final Version VERSION = new Version(0, 2, 0, true); - - private static final Logger logger = LogsCenter.getLogger(MainApp.class); - - protected Ui ui; - protected Logic logic; - protected Storage storage; - protected Model model; - protected Config config; - - @Override - public void init() throws Exception { - logger.info( - "=============================[ Initializing AddressBook ]==========================="); - super.init(); - - AppParameters appParameters = AppParameters.parse(getParameters()); - config = initConfig(appParameters.getConfigPath()); - - UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); - UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = - new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); - storage = new StorageManager(addressBookStorage, userPrefsStorage); - - initLogging(config); - - model = initModelManager(storage, userPrefs); - - logic = new LogicManager(model, storage); - - ui = new UiManager(logic); - } - - /** - * Returns a {@code ModelManager} with the data from {@code storage}'s address book and - * {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book - * is not found, or an empty address book will be used instead if errors occur when reading - * {@code storage}'s address book. - */ - private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; - try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); - } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); - } catch (DataConversionException e) { - logger.warning( - "Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); - } catch (IOException e) { - logger.warning( - "Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); - } - - return new ModelManager(initialData, userPrefs); - } - - private void initLogging(Config config) { - LogsCenter.init(config); - } - - /** - * Returns a {@code Config} using the file at {@code configFilePath}.
- * The default file path {@code Config#DEFAULT_CONFIG_FILE} will be used instead if - * {@code configFilePath} is null. - */ - protected Config initConfig(Path configFilePath) { - Config initializedConfig; - Path configFilePathUsed; - - configFilePathUsed = Config.DEFAULT_CONFIG_FILE; - - if (configFilePath != null) { - logger.info("Custom Config file specified " + configFilePath); - configFilePathUsed = configFilePath; - } - - logger.info("Using config file : " + configFilePathUsed); - - try { - Optional configOptional = ConfigUtil.readConfig(configFilePathUsed); - initializedConfig = configOptional.orElse(new Config()); - } catch (DataConversionException e) { - logger.warning( - "Config file at " + configFilePathUsed + " is not in the correct format. " - + "Using default config properties"); - initializedConfig = new Config(); - } - - // Update config file in case it was missing to begin with or there are new/unused fields - try { - ConfigUtil.saveConfig(initializedConfig, configFilePathUsed); - } catch (IOException e) { - logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); - } - return initializedConfig; - } - - /** - * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs file path, or a - * new {@code UserPrefs} with default configuration if errors occur when reading from the file. - */ - protected UserPrefs initPrefs(UserPrefsStorage storage) { - Path prefsFilePath = storage.getUserPrefsFilePath(); - logger.info("Using prefs file : " + prefsFilePath); - - UserPrefs initializedPrefs; - try { - Optional prefsOptional = storage.readUserPrefs(); - initializedPrefs = prefsOptional.orElse(new UserPrefs()); - } catch (DataConversionException e) { - logger.warning("UserPrefs file at " + prefsFilePath + " is not in the correct format. " - + "Using default user prefs"); - initializedPrefs = new UserPrefs(); - } catch (IOException e) { - logger.warning( - "Problem while reading from the file. Will be starting with an empty AddressBook"); - initializedPrefs = new UserPrefs(); - } - - // Update prefs file in case it was missing to begin with or there are new/unused fields - try { - storage.saveUserPrefs(initializedPrefs); - } catch (IOException e) { - logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); - } - - return initializedPrefs; - } - - @Override - public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); - ui.start(primaryStage); - } - - @Override - public void stop() { - logger.info( - "============================ [ Stopping Address Book ] ============================="); - try { - storage.saveUserPrefs(model.getUserPrefs()); - } catch (IOException e) { - logger.severe("Failed to save preferences " + StringUtil.getDetails(e)); - } - } -} diff --git a/src/main/java/vimification/logic/commands/ListCommand.java b/src/main/java/vimification/logic/commands/ListCommand.java index 939693bddd7..5e80b74ad4f 100644 --- a/src/main/java/vimification/logic/commands/ListCommand.java +++ b/src/main/java/vimification/logic/commands/ListCommand.java @@ -14,7 +14,6 @@ public class ListCommand extends Command { public static final String MESSAGE_SUCCESS = "Listed all persons"; - @Override public CommandResult execute(Model model) { requireNonNull(model); diff --git a/src/main/java/vimification/logic/commands/RemarkCommand.java b/src/main/java/vimification/logic/commands/RemarkCommand.java index 73320e217e5..a473b20c677 100644 --- a/src/main/java/vimification/logic/commands/RemarkCommand.java +++ b/src/main/java/vimification/logic/commands/RemarkCommand.java @@ -1,11 +1,7 @@ package vimification.logic.commands; -<<<<<<< HEAD:src/main/java/seedu/address/logic/commands/RemarkCommand.java -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -======= +import vimification.logic.commands.exceptions.CommandException; import vimification.model.Model; ->>>>>>> b327d0ef68878bc728042fdb72e2a9056a10e60d:src/main/java/vimification/logic/commands/RemarkCommand.java public class RemarkCommand extends Command { diff --git a/src/main/java/vimification/taskui/MainWindow.java b/src/main/java/vimification/taskui/MainWindow.java index c2b704f9658..43107ee0bdf 100644 --- a/src/main/java/vimification/taskui/MainWindow.java +++ b/src/main/java/vimification/taskui/MainWindow.java @@ -1,7 +1,14 @@ package vimification.taskui; +import java.io.IOException; +import javafx.application.Application; +import javafx.application.Platform; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.VBox; // import javafx.scene.control.MenuItem; // import javafx.scene.control.TextInputControl; // import javafx.scene.input.KeyCombination; @@ -10,105 +17,84 @@ import javafx.stage.Stage; import vimification.logic.Logic; - /** * The Main Window. Provides the basic application layout containing a menu bar and space where * other JavaFX elements can be placed. */ -public class MainWindow extends UiPart { +public class MainWindow extends VBox { - private static final String FXML = "MainWindow.fxml"; + private static final String FXML = "Main.fxml"; private Stage primaryStage; private Logic logic; // Independent Ui parts residing in this Ui container private TaskListPanel taskListPanel; - // private ResultDisplay resultDisplay; - // private HelpWindow helpWindow; - - /** - * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. - */ - public MainWindow(Stage primaryStage, Logic logic) { - super(FXML, primaryStage); - - // Set dependencies - this.primaryStage = primaryStage; - this.logic = logic; - - // Configure the UI - // setWindowDefaultSize(logic.getGuiSettings()); - } - - public Stage getPrimaryStage() { - return primaryStage; - } - - /** - * Fills up all the placeholders of this window. - */ - void fillInnerParts() { - // taskListPanel = new TaskListPanel(); - // personListPanelPlaceholder.getChildren().add(taskListPanel.getRoot()); + @FXML + private VBox leftComponent; - // resultDisplay = new ResultDisplay(); - // resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); + @FXML + private VBox rightComponent; - // StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); - // statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); + @FXML + private VBox textBoxComponent; - // CommandBox commandBox = new CommandBox(this::executeCommand); - // commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); - } + @FXML + private TextField textBox; /** - * Closes the application. + * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. */ - @FXML - private void handleExit() { - // GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), - // primaryStage.getHeight(), - // (int) primaryStage.getX(), (int) primaryStage.getY()); - // logic.setGuiSettings(guiSettings); - // helpWindow.hide(); - // primaryStage.hide(); + public MainWindow() { + try { + FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/view/Main.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + + this.setOnKeyPressed(event -> { + if (event.getText().equals(":")) { + textBoxComponent.setVisible(true); + textBox.requestFocus(); + } + }); + + textBoxComponent.setOnKeyPressed(event -> { + if (event.getCode().equals(KeyCode.ESCAPE)) { + textBoxComponent.setVisible(false); + this.requestFocus(); + System.out.println("You escaped"); + } + }); + + textBox.setOnKeyPressed(event -> { + if (event.getCode().equals(KeyCode.ENTER)) { + textBoxComponent.setVisible(false); + this.requestFocus(); + String commandString = textBox.getText(); + System.out.println("Your command is " + commandString); + + // TODO : Create a Parser to parse the command and create a Driver to run it. + if (commandString.equals(":wq!")) { + Platform.exit(); + } + } + }); + + // Set up the ":" key listener to show/hide the text box component + } catch (IOException ex) { + ex.printStackTrace(); + } } - public TaskListPanel getTaskListPanel() { - return taskListPanel; + public Stage getPrimaryStage() { + return primaryStage; } - /** - * Executes the command and returns the result. - * - * @see vimification.logic.Logic#execute(String) - */ - private void executeCommand(String commandText) { - // throws CommandException, ParseException { - // try { - // CommandResult commandResult = logic.execute(commandText); - // logger.info("Result: " + commandResult.getFeedbackToUser()); - // resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); - - // if (commandResult.isShowHelp()) { - // handleHelp(); - // } - - // if (commandResult.isExit()) { - // handleExit(); - // } - - // return commandResult; - // } catch (CommandException | ParseException e) { - // logger.info("Invalid command: " + commandText); - // resultDisplay.setFeedbackToUser(e.getMessage()); - // throw e; - // } + @FXML + public void initialize() { + textBoxComponent.setVisible(false); } - void show() { - primaryStage.show(); - } } diff --git a/src/main/java/vimification/taskui/UiManager.java b/src/main/java/vimification/taskui/UiManager.java deleted file mode 100644 index 3bd8c53f3d6..00000000000 --- a/src/main/java/vimification/taskui/UiManager.java +++ /dev/null @@ -1,90 +0,0 @@ -package vimification.taskui; - -import java.util.logging.Logger; - -import javafx.application.Platform; -import javafx.scene.control.Alert; -import javafx.scene.control.Alert.AlertType; -import javafx.scene.image.Image; -import javafx.stage.Stage; -import vimification.MainApp; -import vimification.commons.core.LogsCenter; -import vimification.commons.util.StringUtil; -import vimification.logic.Logic; - -/** - * The manager of the UI component. - */ -public class UiManager implements Ui { - - public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane"; - - private static final Logger logger = LogsCenter.getLogger(UiManager.class); - private static final String ICON_APPLICATION = "/images/address_book_32.png"; - - private Logic logic; - private MainWindow mainWindow; - - /** - * Creates a {@code UiManager} with the given {@code Logic}. - */ - public UiManager(Logic logic) { - this.logic = logic; - } - - @Override - public void start(Stage primaryStage) { - logger.info("Starting UI..."); - - // Set the application icon. - primaryStage.getIcons().add(getImage(ICON_APPLICATION)); - - try { - mainWindow = new MainWindow(primaryStage, logic); - mainWindow.show(); // This should be called before creating other UI parts - mainWindow.fillInnerParts(); - - } catch (Throwable e) { - logger.severe(StringUtil.getDetails(e)); - showFatalErrorDialogAndShutdown("Fatal error during initializing", e); - } - } - - private Image getImage(String imagePath) { - return new Image(MainApp.class.getResourceAsStream(imagePath)); - } - - void showAlertDialogAndWait(Alert.AlertType type, String title, String headerText, - String contentText) { - showAlertDialogAndWait(mainWindow.getPrimaryStage(), type, title, headerText, contentText); - } - - /** - * Shows an alert dialog on {@code owner} with the given parameters. This method only returns - * after the user has closed the alert dialog. - */ - private static void showAlertDialogAndWait(Stage owner, AlertType type, String title, - String headerText, - String contentText) { - final Alert alert = new Alert(type); - alert.getDialogPane().getStylesheets().add("view/DarkTheme.css"); - alert.initOwner(owner); - alert.setTitle(title); - alert.setHeaderText(headerText); - alert.setContentText(contentText); - alert.getDialogPane().setId(ALERT_DIALOG_PANE_FIELD_ID); - alert.showAndWait(); - } - - /** - * Shows an error alert dialog with {@code title} and error message, {@code e}, and exits the - * application after the user has closed the alert dialog. - */ - private void showFatalErrorDialogAndShutdown(String title, Throwable e) { - logger.severe(title + " " + e.getMessage() + StringUtil.getDetails(e)); - showAlertDialogAndWait(Alert.AlertType.ERROR, title, e.getMessage(), e.toString()); - Platform.exit(); - System.exit(1); - } - -} diff --git a/src/main/resources/view/Main.fxml b/src/main/resources/view/Main.fxml index 4f49f56379b..3c8e092f537 100644 --- a/src/main/resources/view/Main.fxml +++ b/src/main/resources/view/Main.fxml @@ -6,7 +6,7 @@ - + From f9425855ad9f74d5bf3457b886033b5e7186b641 Mon Sep 17 00:00:00 2001 From: liujiayue314 Date: Tue, 14 Mar 2023 15:44:12 +0800 Subject: [PATCH 020/470] Update AB3 storage to be compatible with Task Planner. --- src/main/java/vimification/MainApp.java | 3 +- .../logic/commands/RemarkCommand.java | 7 +- .../storage/AddressBookStorage.java | 48 -------- .../storage/JsonAdaptedDeadline.java | 44 +++++++ .../storage/JsonAdaptedEvent.java | 53 ++++++++ .../storage/JsonAdaptedPerson.java | 114 ------------------ .../vimification/storage/JsonAdaptedTag.java | 48 -------- .../vimification/storage/JsonAdaptedTask.java | 31 +++++ .../vimification/storage/JsonAdaptedTodo.java | 41 +++++++ .../storage/JsonSerializableAddressBook.java | 63 ---------- .../storage/JsonSerializableTaskList.java | 64 ++++++++++ ...orage.java => JsonTaskPlannerStorage.java} | 41 +++---- .../storage/JsonUserPrefsStorage.java | 1 - .../java/vimification/storage/Storage.java | 10 +- .../vimification/storage/StorageManager.java | 38 +++--- .../storage/TaskPlannerStorage.java | 45 +++++++ .../storage/UserPrefsStorage.java | 10 +- 17 files changed, 328 insertions(+), 333 deletions(-) delete mode 100644 src/main/java/vimification/storage/AddressBookStorage.java create mode 100644 src/main/java/vimification/storage/JsonAdaptedDeadline.java create mode 100644 src/main/java/vimification/storage/JsonAdaptedEvent.java delete mode 100644 src/main/java/vimification/storage/JsonAdaptedPerson.java delete mode 100644 src/main/java/vimification/storage/JsonAdaptedTag.java create mode 100644 src/main/java/vimification/storage/JsonAdaptedTask.java create mode 100644 src/main/java/vimification/storage/JsonAdaptedTodo.java delete mode 100644 src/main/java/vimification/storage/JsonSerializableAddressBook.java create mode 100644 src/main/java/vimification/storage/JsonSerializableTaskList.java rename src/main/java/vimification/storage/{JsonAddressBookStorage.java => JsonTaskPlannerStorage.java} (50%) create mode 100644 src/main/java/vimification/storage/TaskPlannerStorage.java diff --git a/src/main/java/vimification/MainApp.java b/src/main/java/vimification/MainApp.java index 986d3a142f1..d8c0d61ef2a 100644 --- a/src/main/java/vimification/MainApp.java +++ b/src/main/java/vimification/MainApp.java @@ -81,7 +81,8 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { Optional addressBookOptional; ReadOnlyAddressBook initialData; try { - addressBookOptional = storage.readAddressBook(); + addressBookOptional = storage. + readAddressBook(); if (!addressBookOptional.isPresent()) { logger.info("Data file not found. Will be starting with a sample AddressBook"); } diff --git a/src/main/java/vimification/logic/commands/RemarkCommand.java b/src/main/java/vimification/logic/commands/RemarkCommand.java index 73320e217e5..15d6f669a80 100644 --- a/src/main/java/vimification/logic/commands/RemarkCommand.java +++ b/src/main/java/vimification/logic/commands/RemarkCommand.java @@ -1,11 +1,8 @@ package vimification.logic.commands; -<<<<<<< HEAD:src/main/java/seedu/address/logic/commands/RemarkCommand.java -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -======= +import vimification.logic.commands.exceptions.CommandException; import vimification.model.Model; ->>>>>>> b327d0ef68878bc728042fdb72e2a9056a10e60d:src/main/java/vimification/logic/commands/RemarkCommand.java + public class RemarkCommand extends Command { diff --git a/src/main/java/vimification/storage/AddressBookStorage.java b/src/main/java/vimification/storage/AddressBookStorage.java deleted file mode 100644 index 5af5286d1ae..00000000000 --- a/src/main/java/vimification/storage/AddressBookStorage.java +++ /dev/null @@ -1,48 +0,0 @@ -package vimification.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import vimification.commons.exceptions.DataConversionException; -import vimification.model.ReadOnlyAddressBook; - -/** - * Represents a storage for {@link vimification.model.AddressBook}. - */ -public interface AddressBookStorage { - - /** - * Returns the file path of the data file. - */ - Path getAddressBookFilePath(); - - /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. Returns {@code Optional.empty()} - * if storage file is not found. - * - * @throws DataConversionException if the data in storage is not in the expected format. - * @throws IOException if there was any problem when reading from the storage. - */ - Optional readAddressBook() throws DataConversionException, IOException; - - /** - * @see #getAddressBookFilePath() - */ - Optional readAddressBook(Path filePath) - throws DataConversionException, IOException; - - /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * - * @param addressBook cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * @see #saveAddressBook(ReadOnlyAddressBook) - */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; - -} diff --git a/src/main/java/vimification/storage/JsonAdaptedDeadline.java b/src/main/java/vimification/storage/JsonAdaptedDeadline.java new file mode 100644 index 00000000000..683c7571cf7 --- /dev/null +++ b/src/main/java/vimification/storage/JsonAdaptedDeadline.java @@ -0,0 +1,44 @@ +package vimification.storage; + +import vimification.commons.exceptions.IllegalValueException; +import vimification.model.task.Description; +import vimification.model.task.Status; +import vimification.model.task.Task; +import vimification.model.task.Date; +import vimification.model.task.Deadline; + +public class JsonAdaptedDeadline extends JsonAdaptedTask { + + private final String deadline; + + public JsonAdaptedDeadline(Deadline task) { + super(task); + deadline = task.getDeadline().value; + } + + public Task toModelType() throws IllegalValueException { + if (description == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Description.class.getSimpleName())); + } + if (!Description.isValidDescription(description)) { + throw new IllegalValueException(Description.MESSAGE_CONSTRAINTS); + } + final Description modelDescription = new Description(description); + + if (status == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Status.class.getSimpleName())); + } + final Status modelStatus = new Status(status); + + if (deadline == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName())); + } + if (!Date.isValidDate(deadline)) { + throw new IllegalValueException(Description.MESSAGE_CONSTRAINTS); + } + final Date modelDate = new Date(deadline); + + return new Deadline(modelDescription, modelStatus, modelDate); + } +} + diff --git a/src/main/java/vimification/storage/JsonAdaptedEvent.java b/src/main/java/vimification/storage/JsonAdaptedEvent.java new file mode 100644 index 00000000000..d0964449af3 --- /dev/null +++ b/src/main/java/vimification/storage/JsonAdaptedEvent.java @@ -0,0 +1,53 @@ +package vimification.storage; + +import vimification.commons.exceptions.IllegalValueException; +import vimification.model.task.Date; +import vimification.model.task.Description; +import vimification.model.task.Event; +import vimification.model.task.Status; +import vimification.model.task.Task; +import vimification.model.task.Type; + +public class JsonAdaptedEvent extends JsonAdaptedTask { + + private final String startDate; + private final String endDate; + + public JsonAdaptedEvent (Event task) { + super(task); + startDate = task.getStartDate().value; + endDate = task.getEndDate().value; + } + + public Task toModelType() throws IllegalValueException { + if (description == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Description.class.getSimpleName())); + } + if (!Description.isValidDescription(description)) { + throw new IllegalValueException(Description.MESSAGE_CONSTRAINTS); + } + final Description modelDescription = new Description(description); + + if (status == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Status.class.getSimpleName())); + } + final Status modelStatus = new Status(status); + if (type == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Type.class.getSimpleName())); + } + if (!Type.isValidType(type)) { + throw new IllegalValueException(Type.MESSAGE_CONSTRAINTS); + } + final Type modelType = new Type(type); + if (startDate == null || endDate == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName())); + } + if (!Date.isValidDate(startDate) || !Date.isValidDate(endDate)) { + throw new IllegalValueException(Description.MESSAGE_CONSTRAINTS); + } + final Date modelStartDate = new Date(startDate); + final Date modelEndDate = new Date(endDate); + + return new Event(modelDescription, modelStatus, modelStartDate, modelEndDate); + } +} diff --git a/src/main/java/vimification/storage/JsonAdaptedPerson.java b/src/main/java/vimification/storage/JsonAdaptedPerson.java deleted file mode 100644 index 8f479ef5f7d..00000000000 --- a/src/main/java/vimification/storage/JsonAdaptedPerson.java +++ /dev/null @@ -1,114 +0,0 @@ -package vimification.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import vimification.commons.exceptions.IllegalValueException; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Person; -import vimification.model.person.Phone; -import vimification.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Person}. - */ -class JsonAdaptedPerson { - - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; - - private final String name; - private final String phone; - private final String email; - private final String address; - private final List tagged = new ArrayList<>(); - - /** - * Constructs a {@code JsonAdaptedPerson} with the given person details. - */ - @JsonCreator - public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, - @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tagged") List tagged) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tagged != null) { - this.tagged.addAll(tagged); - } - } - - /** - * Converts a given {@code Person} into this class for Jackson use. - */ - public JsonAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); - } - - /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted - * person. - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - - if (name == null) { - throw new IllegalValueException( - String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); - } - if (!Name.isValidName(name)) { - throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); - } - final Name modelName = new Name(name); - - if (phone == null) { - throw new IllegalValueException( - String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException( - String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - throw new IllegalValueException( - String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); - } - final Address modelAddress = new Address(address); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - -} diff --git a/src/main/java/vimification/storage/JsonAdaptedTag.java b/src/main/java/vimification/storage/JsonAdaptedTag.java deleted file mode 100644 index e5bc733580a..00000000000 --- a/src/main/java/vimification/storage/JsonAdaptedTag.java +++ /dev/null @@ -1,48 +0,0 @@ -package vimification.storage; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import vimification.commons.exceptions.IllegalValueException; -import vimification.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Tag}. - */ -class JsonAdaptedTag { - - private final String tagName; - - /** - * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}. - */ - @JsonCreator - public JsonAdaptedTag(String tagName) { - this.tagName = tagName; - } - - /** - * Converts a given {@code Tag} into this class for Jackson use. - */ - public JsonAdaptedTag(Tag source) { - tagName = source.tagName; - } - - @JsonValue - public String getTagName() { - return tagName; - } - - /** - * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted tag. - */ - public Tag toModelType() throws IllegalValueException { - if (!Tag.isValidTagName(tagName)) { - throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(tagName); - } - -} diff --git a/src/main/java/vimification/storage/JsonAdaptedTask.java b/src/main/java/vimification/storage/JsonAdaptedTask.java new file mode 100644 index 00000000000..fc43301b7c9 --- /dev/null +++ b/src/main/java/vimification/storage/JsonAdaptedTask.java @@ -0,0 +1,31 @@ +package vimification.storage; + +import vimification.commons.exceptions.IllegalValueException; +import vimification.model.task.Task; + +/** + * Jackson-friendly version of {@link Task}. + */ +public abstract class JsonAdaptedTask { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Task's %s field is missing!"; + public final String description; + public final String status; + public final String type; + + + /** + * Converts a given {@code Task} into this class for Jackson use. + */ + public JsonAdaptedTask(Task task) { + description = task.getDescription().description; + status = task.getStatus().value; + type = task.getType().value; + } + /** + * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted person. + */ + public abstract Task toModelType() throws IllegalValueException; +} diff --git a/src/main/java/vimification/storage/JsonAdaptedTodo.java b/src/main/java/vimification/storage/JsonAdaptedTodo.java new file mode 100644 index 00000000000..c34f99ba27a --- /dev/null +++ b/src/main/java/vimification/storage/JsonAdaptedTodo.java @@ -0,0 +1,41 @@ +package vimification.storage; + +import vimification.commons.exceptions.IllegalValueException; +import vimification.model.task.Description; +import vimification.model.task.Status; +import vimification.model.task.Task; +import vimification.model.task.Todo; +import vimification.model.task.Type; + +public class JsonAdaptedTodo extends JsonAdaptedTask{ + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Task's %s field is missing!"; + + public JsonAdaptedTodo(Todo task) { + super(task); + } + + public Task toModelType() throws IllegalValueException { + if (description == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Description.class.getSimpleName())); + } + if (!Description.isValidDescription(description)) { + throw new IllegalValueException(Description.MESSAGE_CONSTRAINTS); + } + final Description modelDescription = new Description(description); + + if (status == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Status.class.getSimpleName())); + } + final Status modelStatus = new Status(status); + if (type == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Type.class.getSimpleName())); + } + if (!Type.isValidType(type)) { + throw new IllegalValueException(Type.MESSAGE_CONSTRAINTS); + } + final Type modelType = new Type(type); + + return new Todo(modelDescription, modelStatus); + } +} + diff --git a/src/main/java/vimification/storage/JsonSerializableAddressBook.java b/src/main/java/vimification/storage/JsonSerializableAddressBook.java deleted file mode 100644 index ffc257b8c13..00000000000 --- a/src/main/java/vimification/storage/JsonSerializableAddressBook.java +++ /dev/null @@ -1,63 +0,0 @@ -package vimification.storage; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonRootName; - -import vimification.commons.exceptions.IllegalValueException; -import vimification.model.AddressBook; -import vimification.model.ReadOnlyAddressBook; -import vimification.model.person.Person; - -/** - * An Immutable AddressBook that is serializable to JSON format. - */ -@JsonRootName(value = "addressbook") -class JsonSerializableAddressBook { - - public static final String MESSAGE_DUPLICATE_PERSON = - "Persons list contains duplicate person(s)."; - - private final List persons = new ArrayList<>(); - - /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. - */ - @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); - } - - /** - * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. - * - * @param source future changes to this will not affect the created - * {@code JsonSerializableAddressBook}. - */ - public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new) - .collect(Collectors.toList())); - } - - /** - * Converts this address book into the model's {@code AddressBook} object. - * - * @throws IllegalValueException if there were any data constraints violated. - */ - public AddressBook toModelType() throws IllegalValueException { - AddressBook addressBook = new AddressBook(); - for (JsonAdaptedPerson jsonAdaptedPerson : persons) { - Person person = jsonAdaptedPerson.toModelType(); - if (addressBook.hasPerson(person)) { - throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); - } - addressBook.addPerson(person); - } - return addressBook; - } - -} diff --git a/src/main/java/vimification/storage/JsonSerializableTaskList.java b/src/main/java/vimification/storage/JsonSerializableTaskList.java new file mode 100644 index 00000000000..4ec2b77e3a7 --- /dev/null +++ b/src/main/java/vimification/storage/JsonSerializableTaskList.java @@ -0,0 +1,64 @@ +package vimification.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonRootName; + +import vimification.commons.exceptions.IllegalValueException; +import vimification.model.ReadOnlyTaskPlanner; +import vimification.model.TaskPlanner; +import vimification.model.task.Task; +import vimification.model.task.Todo; +import vimification.model.task.Deadline; +import vimification.model.task.Event; + +/** + * An Immutable TaskPlanner that is serializable to JSON format. + */ +@JsonRootName(value = "taskList") +public class JsonSerializableTaskList { + + public static final String MESSAGE_DUPLICATE_PERSON = "Task list contains duplicate task(s)."; + + private final List tasks = new ArrayList<>(); + + + /** + * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}. + */ + public JsonSerializableTaskList(ReadOnlyTaskPlanner source) { + tasks.addAll(source.getTaskList().stream().map(this::toJsonAdaptedTask).collect(Collectors.toList())); + } + + private JsonAdaptedTask toJsonAdaptedTask (Task task) { + if (task instanceof Todo) { + return new JsonAdaptedTodo((Todo) task); + } else if (task instanceof Deadline) { + return new JsonAdaptedDeadline((Deadline) task); + } else { + return new JsonAdaptedEvent((Event) task); + } + } + + /** + * Converts this address book into the model's {@code TaskPlanner} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public TaskPlanner toModelType() throws IllegalValueException { + TaskPlanner taskList = new TaskPlanner(); + for (JsonAdaptedTask jsonAdaptedTask : tasks) { + Task t = jsonAdaptedTask.toModelType(); + if (taskList.hasTask(t)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); + } + taskList.addTask(t); + } + return taskList; + } + +} diff --git a/src/main/java/vimification/storage/JsonAddressBookStorage.java b/src/main/java/vimification/storage/JsonTaskPlannerStorage.java similarity index 50% rename from src/main/java/vimification/storage/JsonAddressBookStorage.java rename to src/main/java/vimification/storage/JsonTaskPlannerStorage.java index fef8959f3b0..883b7dc7ad2 100644 --- a/src/main/java/vimification/storage/JsonAddressBookStorage.java +++ b/src/main/java/vimification/storage/JsonTaskPlannerStorage.java @@ -12,48 +12,47 @@ import vimification.commons.exceptions.IllegalValueException; import vimification.commons.util.FileUtil; import vimification.commons.util.JsonUtil; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; /** - * A class to access AddressBook data stored as a json file on the hard disk. + * A class to access TaskPlanner data stored as a json file on the hard disk. */ -public class JsonAddressBookStorage implements AddressBookStorage { +public class JsonTaskPlannerStorage implements TaskPlannerStorage { - private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class); + private static final Logger logger = LogsCenter.getLogger(JsonTaskPlannerStorage.class); private Path filePath; - public JsonAddressBookStorage(Path filePath) { + public JsonTaskPlannerStorage(Path filePath) { this.filePath = filePath; } - public Path getAddressBookFilePath() { + public Path getTaskListFilePath() { return filePath; } @Override - public Optional readAddressBook() throws DataConversionException { - return readAddressBook(filePath); + public Optional readTaskList() throws DataConversionException { + return readTaskList(filePath); } /** - * Similar to {@link #readAddressBook()}. + * Similar to {@link #readTaskList()}. * * @param filePath location of the data. Cannot be null. * @throws DataConversionException if the file is not in the correct format. */ - public Optional readAddressBook(Path filePath) - throws DataConversionException { + public Optional readTaskList(Path filePath) throws DataConversionException { requireNonNull(filePath); - Optional jsonAddressBook = JsonUtil.readJsonFile( - filePath, JsonSerializableAddressBook.class); - if (!jsonAddressBook.isPresent()) { + Optional jsonTaskList = JsonUtil.readJsonFile( + filePath, JsonSerializableTaskList.class); + if (!jsonTaskList.isPresent()) { return Optional.empty(); } try { - return Optional.of(jsonAddressBook.get().toModelType()); + return Optional.of(jsonTaskList.get().toModelType()); } catch (IllegalValueException ive) { logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); throw new DataConversionException(ive); @@ -61,21 +60,21 @@ public Optional readAddressBook(Path filePath) } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); + public void saveTaskList(ReadOnlyTaskPlanner taskList) throws IOException { + saveTaskList(taskList, filePath); } /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. + * Similar to {@link #saveTaskList(ReadOnlyTaskPlanner)}. * * @param filePath location of the data. Cannot be null. */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); + public void saveTaskList(ReadOnlyTaskPlanner taskList, Path filePath) throws IOException { + requireNonNull(taskList); requireNonNull(filePath); FileUtil.createIfMissing(filePath); - JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath); + JsonUtil.saveJsonFile(new JsonSerializableTaskList(taskList), filePath); } } diff --git a/src/main/java/vimification/storage/JsonUserPrefsStorage.java b/src/main/java/vimification/storage/JsonUserPrefsStorage.java index e96f7b0d191..3da6fc64d2d 100644 --- a/src/main/java/vimification/storage/JsonUserPrefsStorage.java +++ b/src/main/java/vimification/storage/JsonUserPrefsStorage.java @@ -32,7 +32,6 @@ public Optional readUserPrefs() throws DataConversionException { /** * Similar to {@link #readUserPrefs()} - * * @param prefsFilePath location of the data. Cannot be null. * @throws DataConversionException if the file format is not as expected. */ diff --git a/src/main/java/vimification/storage/Storage.java b/src/main/java/vimification/storage/Storage.java index 0efcfec7b64..7998f757d5e 100644 --- a/src/main/java/vimification/storage/Storage.java +++ b/src/main/java/vimification/storage/Storage.java @@ -5,14 +5,14 @@ import java.util.Optional; import vimification.commons.exceptions.DataConversionException; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; import vimification.model.ReadOnlyUserPrefs; import vimification.model.UserPrefs; /** * API of the Storage component */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { +public interface Storage extends TaskPlannerStorage, UserPrefsStorage { @Override Optional readUserPrefs() throws DataConversionException, IOException; @@ -21,12 +21,12 @@ public interface Storage extends AddressBookStorage, UserPrefsStorage { void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; @Override - Path getAddressBookFilePath(); + Path getTaskListFilePath(); @Override - Optional readAddressBook() throws DataConversionException, IOException; + Optional readTaskList() throws DataConversionException, IOException; @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + void saveTaskList(ReadOnlyTaskPlanner taskList) throws IOException; } diff --git a/src/main/java/vimification/storage/StorageManager.java b/src/main/java/vimification/storage/StorageManager.java index dafae72ae6c..56618c962af 100644 --- a/src/main/java/vimification/storage/StorageManager.java +++ b/src/main/java/vimification/storage/StorageManager.java @@ -7,26 +7,24 @@ import vimification.commons.core.LogsCenter; import vimification.commons.exceptions.DataConversionException; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; import vimification.model.ReadOnlyUserPrefs; import vimification.model.UserPrefs; /** - * Manages storage of AddressBook data in local storage. + * Manages storage of TaskPlanner data in local storage. */ public class StorageManager implements Storage { private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; + private TaskPlannerStorage taskPlannerStorage; private UserPrefsStorage userPrefsStorage; /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and - * {@code UserPrefStorage}. + * Creates a {@code StorageManager} with the given {@code TaskPlannerStorage} and {@code UserPrefStorage}. */ - public StorageManager(AddressBookStorage addressBookStorage, - UserPrefsStorage userPrefsStorage) { - this.addressBookStorage = addressBookStorage; + public StorageManager(TaskPlannerStorage taskPlannerStorage, UserPrefsStorage userPrefsStorage) { + this.taskPlannerStorage = taskPlannerStorage; this.userPrefsStorage = userPrefsStorage; } @@ -48,35 +46,33 @@ public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { } - // ================ AddressBook methods ============================== + // ================ TaskPlanner methods ============================== @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); + public Path getTaskListFilePath() { + return taskPlannerStorage.getTaskListFilePath(); } @Override - public Optional readAddressBook() - throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); + public Optional readTaskList() throws DataConversionException, IOException { + return readTaskList(taskPlannerStorage.getTaskListFilePath()); } @Override - public Optional readAddressBook(Path filePath) - throws DataConversionException, IOException { + public Optional readTaskList(Path filePath) throws DataConversionException, IOException { logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); + return taskPlannerStorage.readTaskList(filePath); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); + public void saveTaskList(ReadOnlyTaskPlanner taskPlanner) throws IOException { + saveTaskList(taskPlanner, taskPlannerStorage.getTaskListFilePath()); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { + public void saveTaskList(ReadOnlyTaskPlanner taskPlanner, Path filePath) throws IOException { logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); + taskPlannerStorage.saveTaskList(taskPlanner, filePath); } } diff --git a/src/main/java/vimification/storage/TaskPlannerStorage.java b/src/main/java/vimification/storage/TaskPlannerStorage.java new file mode 100644 index 00000000000..5e2f19dd317 --- /dev/null +++ b/src/main/java/vimification/storage/TaskPlannerStorage.java @@ -0,0 +1,45 @@ +package vimification.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import vimification.commons.exceptions.DataConversionException; +import vimification.model.ReadOnlyTaskPlanner; + +/** + * Represents a storage for {@link vimification.model.TaskList}. + */ +public interface TaskPlannerStorage { + + /** + * Returns the file path of the data file. + */ + Path getTaskListFilePath(); + + /** + * Returns TaskPlanner data as a {@link ReadOnlyTaskPlanner}. + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readTaskList() throws DataConversionException, IOException; + + /** + * @see #getTaskListFilePath() + */ + Optional readTaskList(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyTaskPlanner} to the storage. + * @param taskList cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveTaskList(ReadOnlyTaskPlanner taskList) throws IOException; + + /** + * @see #saveTaskList(ReadOnlyTaskPlanner) + */ + void saveTaskList(ReadOnlyTaskPlanner taskList, Path filePath) throws IOException; + +} diff --git a/src/main/java/vimification/storage/UserPrefsStorage.java b/src/main/java/vimification/storage/UserPrefsStorage.java index 30174fb5406..1b21497b382 100644 --- a/src/main/java/vimification/storage/UserPrefsStorage.java +++ b/src/main/java/vimification/storage/UserPrefsStorage.java @@ -1,4 +1,4 @@ -package vimification.storage; +package seedu.address.storage; import java.io.IOException; import java.nio.file.Path; @@ -9,7 +9,7 @@ import vimification.model.UserPrefs; /** - * Represents a storage for {@link vimification.model.UserPrefs}. + * Represents a storage for {@link seedu.address.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -19,9 +19,8 @@ public interface UserPrefsStorage { Path getUserPrefsFilePath(); /** - * Returns UserPrefs data from storage. Returns {@code Optional.empty()} if storage file is not - * found. - * + * Returns UserPrefs data from storage. + * Returns {@code Optional.empty()} if storage file is not found. * @throws DataConversionException if the data in storage is not in the expected format. * @throws IOException if there was any problem when reading from the storage. */ @@ -29,7 +28,6 @@ public interface UserPrefsStorage { /** * Saves the given {@link vimification.model.ReadOnlyUserPrefs} to the storage. - * * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ From 421cf33ab909fc1db16aa5fd6c03fc8034f58708 Mon Sep 17 00:00:00 2001 From: liujiayue314 Date: Tue, 14 Mar 2023 16:02:10 +0800 Subject: [PATCH 021/470] Update model to be compatible with Task Planner Added: Description, Type, Date, Status(to represent task done or not done) class. Task, Todo, Deadline and Event class --- src/main/java/vimification/MainApp.java | 6 +- src/main/java/vimification/logic/Logic.java | 5 +- .../java/vimification/logic/LogicManager.java | 5 +- .../logic/commands/AddCommand.java | 1 - .../logic/commands/DeleteCommand.java | 1 - .../logic/commands/EditCommand.java | 5 - .../logic/commands/FindCommand.java | 2 +- .../logic/parser/AddCommandParser.java | 5 - .../logic/parser/FindCommandParser.java | 2 +- .../vimification/logic/parser/ParserUtil.java | 4 - .../java/vimification/model/AddressBook.java | 119 --------------- src/main/java/vimification/model/Model.java | 42 ++--- .../java/vimification/model/ModelManager.java | 91 ++++++----- .../model/ReadOnlyAddressBook.java | 17 --- .../model/ReadOnlyTaskPlanner.java | 17 +++ .../vimification/model/ReadOnlyUserPrefs.java | 2 +- .../java/vimification/model/TaskPlanner.java | 119 +++++++++++++++ .../java/vimification/model/UserPrefs.java | 25 ++- .../vimification/model/person/Address.java | 58 ------- .../java/vimification/model/person/Email.java | 79 ---------- .../java/vimification/model/person/Name.java | 59 ------- .../vimification/model/person/Person.java | 123 --------------- .../java/vimification/model/person/Phone.java | 53 ------- .../model/person/UniquePersonList.java | 136 ----------------- .../exceptions/PersonNotFoundException.java | 7 - src/main/java/vimification/model/tag/Tag.java | 54 ------- .../java/vimification/model/task/Date.java | 50 ++++++ .../vimification/model/task/Deadline.java | 24 +++ .../vimification/model/task/Description.java | 54 +++++++ .../java/vimification/model/task/Event.java | 32 ++++ .../NameContainsKeywordsPredicate.java | 12 +- .../java/vimification/model/task/Status.java | 47 ++++++ .../java/vimification/model/task/Task.java | 85 +++++++++++ .../java/vimification/model/task/Todo.java | 16 ++ .../java/vimification/model/task/Type.java | 34 +++++ .../model/task/UniqueTaskList.java | 144 ++++++++++++++++++ .../exceptions/DuplicateTaskException.java} | 6 +- .../exceptions/TaskNotFoundException.java | 7 + .../model/util/SampleDataUtil.java | 66 -------- .../storage/AddressBookStorage.java | 16 +- .../storage/JsonAdaptedPerson.java | 5 - .../storage/JsonAddressBookStorage.java | 12 +- .../storage/JsonSerializableAddressBook.java | 7 +- .../java/vimification/storage/Storage.java | 6 +- .../vimification/storage/StorageManager.java | 10 +- src/main/java/vimification/ui/PersonCard.java | 1 - .../java/vimification/ui/PersonListPanel.java | 1 - .../vimification/logic/LogicManagerTest.java | 5 +- .../commands/AddCommandIntegrationTest.java | 1 - .../logic/commands/AddCommandTest.java | 9 +- .../logic/commands/CommandTestUtil.java | 3 +- .../logic/commands/DeleteCommandTest.java | 1 - .../logic/commands/EditCommandTest.java | 1 - .../logic/commands/FindCommandTest.java | 2 +- .../logic/parser/AddCommandParserTest.java | 5 - .../logic/parser/AddressBookParserTest.java | 3 +- .../logic/parser/EditCommandParserTest.java | 4 - .../logic/parser/FindCommandParserTest.java | 2 +- .../logic/parser/ParserUtilTest.java | 4 - .../vimification/model/AddressBookTest.java | 9 +- .../vimification/model/ModelManagerTest.java | 2 +- .../NameContainsKeywordsPredicateTest.java | 1 + .../model/person/UniquePersonListTest.java | 15 +- .../storage/JsonAdaptedPersonTest.java | 4 - .../storage/JsonAddressBookStorageTest.java | 8 +- .../storage/StorageManagerTest.java | 4 +- .../testutil/AddressBookBuilder.java | 1 - .../testutil/EditPersonDescriptorBuilder.java | 5 - .../vimification/testutil/PersonBuilder.java | 5 - .../vimification/testutil/PersonUtil.java | 1 - .../java/vimification/testutil/TestUtil.java | 1 - .../vimification/testutil/TypicalPersons.java | 1 - 72 files changed, 784 insertions(+), 985 deletions(-) delete mode 100644 src/main/java/vimification/model/AddressBook.java delete mode 100644 src/main/java/vimification/model/ReadOnlyAddressBook.java create mode 100644 src/main/java/vimification/model/ReadOnlyTaskPlanner.java create mode 100644 src/main/java/vimification/model/TaskPlanner.java delete mode 100644 src/main/java/vimification/model/person/Address.java delete mode 100644 src/main/java/vimification/model/person/Email.java delete mode 100644 src/main/java/vimification/model/person/Name.java delete mode 100644 src/main/java/vimification/model/person/Person.java delete mode 100644 src/main/java/vimification/model/person/Phone.java delete mode 100644 src/main/java/vimification/model/person/UniquePersonList.java delete mode 100644 src/main/java/vimification/model/person/exceptions/PersonNotFoundException.java delete mode 100644 src/main/java/vimification/model/tag/Tag.java create mode 100644 src/main/java/vimification/model/task/Date.java create mode 100644 src/main/java/vimification/model/task/Deadline.java create mode 100644 src/main/java/vimification/model/task/Description.java create mode 100644 src/main/java/vimification/model/task/Event.java rename src/main/java/vimification/model/{person => task}/NameContainsKeywordsPredicate.java (67%) create mode 100644 src/main/java/vimification/model/task/Status.java create mode 100644 src/main/java/vimification/model/task/Task.java create mode 100644 src/main/java/vimification/model/task/Todo.java create mode 100644 src/main/java/vimification/model/task/Type.java create mode 100644 src/main/java/vimification/model/task/UniqueTaskList.java rename src/main/java/vimification/model/{person/exceptions/DuplicatePersonException.java => task/exceptions/DuplicateTaskException.java} (58%) create mode 100644 src/main/java/vimification/model/task/exceptions/TaskNotFoundException.java delete mode 100644 src/main/java/vimification/model/util/SampleDataUtil.java diff --git a/src/main/java/vimification/MainApp.java b/src/main/java/vimification/MainApp.java index 986d3a142f1..591771c7208 100644 --- a/src/main/java/vimification/MainApp.java +++ b/src/main/java/vimification/MainApp.java @@ -18,7 +18,7 @@ import vimification.model.AddressBook; import vimification.model.Model; import vimification.model.ModelManager; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; import vimification.model.ReadOnlyUserPrefs; import vimification.model.UserPrefs; import vimification.model.util.SampleDataUtil; @@ -78,8 +78,8 @@ public void init() throws Exception { * {@code storage}'s address book. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional addressBookOptional; + ReadOnlyTaskPlanner initialData; try { addressBookOptional = storage.readAddressBook(); if (!addressBookOptional.isPresent()) { diff --git a/src/main/java/vimification/logic/Logic.java b/src/main/java/vimification/logic/Logic.java index 93a9a880a42..bac6167047b 100644 --- a/src/main/java/vimification/logic/Logic.java +++ b/src/main/java/vimification/logic/Logic.java @@ -7,8 +7,7 @@ import vimification.logic.commands.CommandResult; import vimification.logic.commands.exceptions.CommandException; import vimification.logic.parser.ParseException; -import vimification.model.ReadOnlyAddressBook; -import vimification.model.person.Person; +import vimification.model.ReadOnlyTaskPlanner; /** * API of the Logic component @@ -29,7 +28,7 @@ public interface Logic { * * @see vimification.model.Model#getAddressBook() */ - ReadOnlyAddressBook getAddressBook(); + ReadOnlyTaskPlanner getAddressBook(); /** Returns an unmodifiable view of the filtered list of persons */ ObservableList getFilteredPersonList(); diff --git a/src/main/java/vimification/logic/LogicManager.java b/src/main/java/vimification/logic/LogicManager.java index 845385122a4..6afd0f39b88 100644 --- a/src/main/java/vimification/logic/LogicManager.java +++ b/src/main/java/vimification/logic/LogicManager.java @@ -13,8 +13,7 @@ import vimification.logic.parser.AddressBookParser; import vimification.logic.parser.ParseException; import vimification.model.Model; -import vimification.model.ReadOnlyAddressBook; -import vimification.model.person.Person; +import vimification.model.ReadOnlyTaskPlanner; import vimification.storage.Storage; /** @@ -55,7 +54,7 @@ public CommandResult execute(String commandText) throws CommandException, ParseE } @Override - public ReadOnlyAddressBook getAddressBook() { + public ReadOnlyTaskPlanner getAddressBook() { return model.getAddressBook(); } diff --git a/src/main/java/vimification/logic/commands/AddCommand.java b/src/main/java/vimification/logic/commands/AddCommand.java index bfa876a760b..5b6ea6321e0 100644 --- a/src/main/java/vimification/logic/commands/AddCommand.java +++ b/src/main/java/vimification/logic/commands/AddCommand.java @@ -9,7 +9,6 @@ import vimification.logic.commands.exceptions.CommandException; import vimification.model.Model; -import vimification.model.person.Person; /** * Adds a person to the address book. diff --git a/src/main/java/vimification/logic/commands/DeleteCommand.java b/src/main/java/vimification/logic/commands/DeleteCommand.java index ce7587f0953..dfc774a785d 100644 --- a/src/main/java/vimification/logic/commands/DeleteCommand.java +++ b/src/main/java/vimification/logic/commands/DeleteCommand.java @@ -8,7 +8,6 @@ import vimification.commons.core.index.Index; import vimification.logic.commands.exceptions.CommandException; import vimification.model.Model; -import vimification.model.person.Person; /** * Deletes a person identified using it's displayed index from the address book. diff --git a/src/main/java/vimification/logic/commands/EditCommand.java b/src/main/java/vimification/logic/commands/EditCommand.java index a5d922683dd..28d61a0894d 100644 --- a/src/main/java/vimification/logic/commands/EditCommand.java +++ b/src/main/java/vimification/logic/commands/EditCommand.java @@ -19,11 +19,6 @@ import vimification.commons.util.CollectionUtil; import vimification.logic.commands.exceptions.CommandException; import vimification.model.Model; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Person; -import vimification.model.person.Phone; import vimification.model.tag.Tag; /** diff --git a/src/main/java/vimification/logic/commands/FindCommand.java b/src/main/java/vimification/logic/commands/FindCommand.java index 1a0356a4318..818015eedda 100644 --- a/src/main/java/vimification/logic/commands/FindCommand.java +++ b/src/main/java/vimification/logic/commands/FindCommand.java @@ -4,7 +4,7 @@ import vimification.commons.core.Messages; import vimification.model.Model; -import vimification.model.person.NameContainsKeywordsPredicate; +import vimification.model.task.NameContainsKeywordsPredicate; /** * Finds and lists all persons in address book whose name contains any of the argument keywords. diff --git a/src/main/java/vimification/logic/parser/AddCommandParser.java b/src/main/java/vimification/logic/parser/AddCommandParser.java index 380e60794fe..a7cfea45df9 100644 --- a/src/main/java/vimification/logic/parser/AddCommandParser.java +++ b/src/main/java/vimification/logic/parser/AddCommandParser.java @@ -11,11 +11,6 @@ import java.util.stream.Stream; import vimification.logic.commands.AddCommand; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Person; -import vimification.model.person.Phone; import vimification.model.tag.Tag; /** diff --git a/src/main/java/vimification/logic/parser/FindCommandParser.java b/src/main/java/vimification/logic/parser/FindCommandParser.java index d4af96d1ad1..b9fe87cf193 100644 --- a/src/main/java/vimification/logic/parser/FindCommandParser.java +++ b/src/main/java/vimification/logic/parser/FindCommandParser.java @@ -5,7 +5,7 @@ import java.util.Arrays; import vimification.logic.commands.FindCommand; -import vimification.model.person.NameContainsKeywordsPredicate; +import vimification.model.task.NameContainsKeywordsPredicate; /** * Parses input arguments and creates a new FindCommand object diff --git a/src/main/java/vimification/logic/parser/ParserUtil.java b/src/main/java/vimification/logic/parser/ParserUtil.java index 8c0a0eee81c..b3c018c75b3 100644 --- a/src/main/java/vimification/logic/parser/ParserUtil.java +++ b/src/main/java/vimification/logic/parser/ParserUtil.java @@ -8,10 +8,6 @@ import vimification.commons.core.index.Index; import vimification.commons.util.StringUtil; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Phone; import vimification.model.tag.Tag; /** diff --git a/src/main/java/vimification/model/AddressBook.java b/src/main/java/vimification/model/AddressBook.java deleted file mode 100644 index 08f81c6a263..00000000000 --- a/src/main/java/vimification/model/AddressBook.java +++ /dev/null @@ -1,119 +0,0 @@ -package vimification.model; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import javafx.collections.ObservableList; -import vimification.model.person.Person; -import vimification.model.person.UniquePersonList; - -/** - * Wraps all data at the address-book level 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(); - } - - public AddressBook() {} - - /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); - resetData(toBeCopied); - } - - //// list overwrite operations - - /** - * 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); - } - - /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. - */ - public void resetData(ReadOnlyAddressBook newData) { - requireNonNull(newData); - - setPersons(newData.getPersonList()); - } - - //// person-level operations - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); - } - - /** - * Adds a person to the address book. The person must not already exist in the address book. - */ - public void addPerson(Person p) { - persons.add(p); - } - - /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the address book. The person identity of {@code editedPerson} - * must not be the same as another existing person in the address book. - */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); - - persons.setPerson(target, editedPerson); - } - - /** - * Removes {@code key} from this {@code AddressBook}. {@code key} must exist in the address - * book. - */ - public void removePerson(Person key) { - persons.remove(key); - } - - //// util methods - - @Override - public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later - } - - @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); - } - - @Override - public int hashCode() { - return persons.hashCode(); - } -} diff --git a/src/main/java/vimification/model/Model.java b/src/main/java/vimification/model/Model.java index 86738619eec..a126224d39a 100644 --- a/src/main/java/vimification/model/Model.java +++ b/src/main/java/vimification/model/Model.java @@ -5,14 +5,14 @@ import javafx.collections.ObservableList; import vimification.commons.core.GuiSettings; -import vimification.model.person.Person; +import vimification.model.task.Task; /** * The API of the Model component. */ public interface Model { /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; /** * Replaces user prefs data with the data in {@code userPrefs}. @@ -37,50 +37,54 @@ public interface Model { /** * Returns the user prefs' address book file path. */ - Path getAddressBookFilePath(); + Path getTaskListFilePath(); /** * Sets the user prefs' address book file path. */ - void setAddressBookFilePath(Path addressBookFilePath); + void setTaskListFilePath(Path taskListFilePath); /** * Replaces address book data with the data in {@code addressBook}. */ - void setAddressBook(ReadOnlyAddressBook addressBook); + void setTaskList(ReadOnlyTaskPlanner taskList); - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); + /** Returns the TaskPlanner */ + ReadOnlyTaskPlanner getTaskList(); /** * Returns true if a person with the same identity as {@code person} exists in the address book. */ - boolean hasPerson(Person person); + boolean hasTask(Task task); /** - * Deletes the given person. The person must exist in the address book. + * Deletes the given person. + * The person must exist in the address book. */ - void deletePerson(Person target); + void deleteTask(Task task); /** - * Adds the given person. {@code person} must not already exist in the address book. + * Adds the given person. + * {@code person} must not already exist in the address book. */ - void addPerson(Person person); + void addTask(Task task); /** - * Replaces the given person {@code target} with {@code editedPerson}. {@code target} must exist - * in the address book. The person identity of {@code editedPerson} must not be the same as - * another existing person in the address book. + * Replaces the given person {@code target} with {@code editedPerson}. + * {@code target} must exist in the address book. + * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. */ - void setPerson(Person target, Person editedPerson); + void setTask(Task target, Task editedTask); /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); + ObservableList getFilteredTaskList(); + void markTask(Task task); + + void unmarkTask(Task task); /** * Updates the filter of the filtered person list to filter by the given {@code predicate}. - * * @throws NullPointerException if {@code predicate} is null. */ - void updateFilteredPersonList(Predicate predicate); + void updateFilteredTaskList(Predicate predicate); } diff --git a/src/main/java/vimification/model/ModelManager.java b/src/main/java/vimification/model/ModelManager.java index fd30101f299..476e7b6b67c 100644 --- a/src/main/java/vimification/model/ModelManager.java +++ b/src/main/java/vimification/model/ModelManager.java @@ -11,7 +11,7 @@ import javafx.collections.transformation.FilteredList; import vimification.commons.core.GuiSettings; import vimification.commons.core.LogsCenter; -import vimification.model.person.Person; +import vimification.model.task.Task; /** * Represents the in-memory model of the address book data. @@ -19,30 +19,28 @@ public class ModelManager implements Model { private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - private final AddressBook addressBook; + private final TaskPlanner taskPlanner; private final UserPrefs userPrefs; - private final FilteredList filteredPersons; + private final FilteredList filteredTasks; /** * Initializes a ModelManager with the given addressBook and userPrefs. */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - requireAllNonNull(addressBook, userPrefs); + public ModelManager(ReadOnlyTaskPlanner taskList, ReadOnlyUserPrefs userPrefs) { + requireAllNonNull(taskList, userPrefs); - logger.fine( - "Initializing with address book: " + addressBook + " and user prefs " + userPrefs); + logger.fine("Initializing with address book: " + taskList + " and user prefs " + userPrefs); - this.addressBook = new AddressBook(addressBook); + this.taskPlanner = new TaskPlanner(taskList); this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); + filteredTasks = new FilteredList<>(this.taskPlanner.getTaskList()); } public ModelManager() { - this(new AddressBook(), new UserPrefs()); + this(new TaskPlanner(), new UserPrefs()); } - // =========== UserPrefs - // ================================================================================== + //=========== UserPrefs ================================================================================== @Override public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { @@ -67,69 +65,78 @@ public void setGuiSettings(GuiSettings guiSettings) { } @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); + public Path getTaskListFilePath() { + return userPrefs.getTaskListFilePath(); } @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); + public void setTaskListFilePath(Path taskListFilePath) { + requireNonNull(taskListFilePath); + userPrefs.setTaskListFilePath(taskListFilePath); } - // =========== AddressBook - // ================================================================================ + //=========== TaskPlanner ================================================================================ @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); + public void setTaskList(ReadOnlyTaskPlanner taskList) { + this.taskPlanner.resetData(taskList); } @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; + public ReadOnlyTaskPlanner getTaskList() { + return taskPlanner; } @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); + public boolean hasTask(Task t) { + requireNonNull(t); + return taskPlanner.hasTask(t); } @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); + public void deleteTask(Task target) { + taskPlanner.removeTask(target); } @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + public void addTask(Task t) { + taskPlanner.addTask(t); + updateFilteredTaskList(PREDICATE_SHOW_ALL_PERSONS); } @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); + public void setTask(Task target, Task editedTask) { + requireAllNonNull(target, editedTask); + taskPlanner.setTask(target, editedTask); + } - addressBook.setPerson(target, editedPerson); + @Override + public void markTask(Task task) { + requireNonNull(task); + taskPlanner.markTask(task); + } + + @Override + public void unmarkTask(Task task) { + requireNonNull(task); + taskPlanner.unmarkTask(task); } - // =========== Filtered Person List Accessors - // ============================================================= + //=========== Filtered Person List Accessors ============================================================= /** * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of * {@code versionedAddressBook} */ @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; + public ObservableList getFilteredTaskList() { + return filteredTasks; } @Override - public void updateFilteredPersonList(Predicate predicate) { + public void updateFilteredTaskList(Predicate predicate) { requireNonNull(predicate); - filteredPersons.setPredicate(predicate); + filteredTasks.setPredicate(predicate); } @Override @@ -146,9 +153,9 @@ public boolean equals(Object obj) { // state check ModelManager other = (ModelManager) obj; - return addressBook.equals(other.addressBook) + return taskPlanner.equals(other.taskPlanner) && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); + && filteredTasks.equals(other.filteredTasks); } } diff --git a/src/main/java/vimification/model/ReadOnlyAddressBook.java b/src/main/java/vimification/model/ReadOnlyAddressBook.java deleted file mode 100644 index fdff0254a8a..00000000000 --- a/src/main/java/vimification/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,17 +0,0 @@ -package vimification.model; - -import javafx.collections.ObservableList; -import vimification.model.person.Person; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - /** - * Returns an unmodifiable view of the persons list. This list will not contain any duplicate - * persons. - */ - ObservableList getPersonList(); - -} diff --git a/src/main/java/vimification/model/ReadOnlyTaskPlanner.java b/src/main/java/vimification/model/ReadOnlyTaskPlanner.java new file mode 100644 index 00000000000..b70bb714683 --- /dev/null +++ b/src/main/java/vimification/model/ReadOnlyTaskPlanner.java @@ -0,0 +1,17 @@ +package vimification.model; + +import javafx.collections.ObservableList; +import vimification.model.task.Task; + +/** + * Unmodifiable view of an address book + */ +public interface ReadOnlyTaskPlanner { + + /** + * Returns an unmodifiable view of the persons list. + * This list will not contain any duplicate persons. + */ + ObservableList getTaskList(); + +} diff --git a/src/main/java/vimification/model/ReadOnlyUserPrefs.java b/src/main/java/vimification/model/ReadOnlyUserPrefs.java index ab1cf5731d6..00f2ffc0525 100644 --- a/src/main/java/vimification/model/ReadOnlyUserPrefs.java +++ b/src/main/java/vimification/model/ReadOnlyUserPrefs.java @@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getTaskListFilePath(); } diff --git a/src/main/java/vimification/model/TaskPlanner.java b/src/main/java/vimification/model/TaskPlanner.java new file mode 100644 index 00000000000..84d85b71175 --- /dev/null +++ b/src/main/java/vimification/model/TaskPlanner.java @@ -0,0 +1,119 @@ +package vimification.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import vimification.model.task.Task; +import vimification.model.task.UniqueTaskList; + +/** + * Wraps all data at the address-book level + * Duplicates are not allowed (by .isSamePerson comparison) + */ +public class TaskPlanner implements ReadOnlyTaskPlanner { + + private final UniqueTaskList tasks; + + /* + * 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. + */ + { + tasks = new UniqueTaskList(); + } + + public TaskPlanner() {} + + /** + * Creates an TaskPlanner using the Persons in the {@code toBeCopied} + */ + public TaskPlanner(ReadOnlyTaskPlanner toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the person list with {@code persons}. + * {@code persons} must not contain duplicate persons. + */ + public void setTasks(List tasks) { + this.tasks.setTasks(tasks); + } + + /** + * Resets the existing data of this {@code TaskPlanner} with {@code newData}. + */ + public void resetData(ReadOnlyTaskPlanner newData) { + requireNonNull(newData); + + setTasks(newData.getTaskList()); + } + + //// person-level operations + + /** + * Returns true if a person with the same identity as {@code person} exists in the address book. + */ + public boolean hasTask(Task t) { + requireNonNull(t); + return tasks.contains(t); + } + + /** + * Adds a person to the address book. + * The person must not already exist in the address book. + */ + public void addTask(Task t) { + tasks.add(t); + } + + public void markTask(Task t) { tasks.mark(t);} + + public void unmarkTask(Task t) { tasks.unmark(t);} + + /** + * Replaces the given person {@code target} in the list with {@code editedPerson}. + * {@code target} must exist in the address book. + * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + */ + public void setTask(Task target, Task editedTask) { + requireNonNull(editedTask); + + tasks.setTasks(target, editedTask); + } + + /** + * Removes {@code key} from this {@code TaskPlanner}. + * {@code key} must exist in the address book. + */ + public void removeTask(Task key) { + tasks.remove(key); + } + + //// util methods + + @Override + public String toString() { + return tasks.asUnmodifiableObservableList().size() + " persons"; + // TODO: refine later + } + + @Override + public ObservableList getTaskList() { + return tasks.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskPlanner // instanceof handles nulls + && tasks.equals(((TaskPlanner) other).tasks)); + } +} diff --git a/src/main/java/vimification/model/UserPrefs.java b/src/main/java/vimification/model/UserPrefs.java index 3a770d6513e..48348974d65 100644 --- a/src/main/java/vimification/model/UserPrefs.java +++ b/src/main/java/vimification/model/UserPrefs.java @@ -4,7 +4,6 @@ import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Objects; import vimification.commons.core.GuiSettings; @@ -14,7 +13,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data", "addressbook.json"); + private Path taskListFilePath = Paths.get("data" , "tasklist.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +34,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setTaskListFilePath(newUserPrefs.getTaskListFilePath()); } public GuiSettings getGuiSettings() { @@ -47,13 +46,13 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getTaskListFilePath() { + return taskListFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setTaskListFilePath(Path taskListFilePath) { + requireNonNull(taskListFilePath); + this.taskListFilePath = taskListFilePath; } @Override @@ -61,26 +60,22 @@ public boolean equals(Object other) { if (other == this) { return true; } - if (!(other instanceof UserPrefs)) { // this handles null as well. + if (!(other instanceof UserPrefs)) { //this handles null as well. return false; } UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && taskListFilePath.equals(o.taskListFilePath); } - @Override - public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); - } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + taskListFilePath); return sb.toString(); } diff --git a/src/main/java/vimification/model/person/Address.java b/src/main/java/vimification/model/person/Address.java deleted file mode 100644 index 2800d5fde0d..00000000000 --- a/src/main/java/vimification/model/person/Address.java +++ /dev/null @@ -1,58 +0,0 @@ -package vimification.model.person; - -import static java.util.Objects.requireNonNull; -import static vimification.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's address in the address book. Guarantees: immutable; is valid as declared in - * {@link #isValidAddress(String)} - */ -public class Address { - - public static final String MESSAGE_CONSTRAINTS = - "Addresses can take any values, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, otherwise " " (a blank string) - * becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[^\\s].*"; - - public final String value; - - /** - * Constructs an {@code Address}. - * - * @param address A valid address. - */ - public Address(String address) { - requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); - value = address; - } - - /** - * Returns true if a given string is a valid email. - */ - public static boolean isValidAddress(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/vimification/model/person/Email.java b/src/main/java/vimification/model/person/Email.java deleted file mode 100644 index 2fa1ee2a1f3..00000000000 --- a/src/main/java/vimification/model/person/Email.java +++ /dev/null @@ -1,79 +0,0 @@ -package vimification.model.person; - -import static java.util.Objects.requireNonNull; -import static vimification.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's email in the address book. Guarantees: immutable; is valid as declared in - * {@link #isValidEmail(String)} - */ -public class Email { - - private static final String SPECIAL_CHARACTERS = "+_.-"; - public static final String MESSAGE_CONSTRAINTS = - "Emails should be of the format local-part@domain " - + "and adhere to the following constraints:\n" - + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " - + "the parentheses, (" + SPECIAL_CHARACTERS - + "). The local-part may not start or end with any special " - + "characters.\n" - + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels " - + "separated by periods.\n" - + "The domain name must:\n" - + " - end with a domain label at least 2 characters long\n" - + " - have each domain label start and end with alphanumeric characters\n" - + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any."; - // alphanumeric and special characters - private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters - // except underscore - private static final String LOCAL_PART_REGEX = - "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]" - + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE - + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At - // least - // two - // chars - private static final String DOMAIN_REGEX = - "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX; - public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX; - - public final String value; - - /** - * Constructs an {@code Email}. - * - * @param email A valid email address. - */ - public Email(String email) { - requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); - value = email; - } - - /** - * Returns if a given string is a valid email. - */ - public static boolean isValidEmail(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Email // instanceof handles nulls - && value.equals(((Email) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/vimification/model/person/Name.java b/src/main/java/vimification/model/person/Name.java deleted file mode 100644 index afce9d7d3f4..00000000000 --- a/src/main/java/vimification/model/person/Name.java +++ /dev/null @@ -1,59 +0,0 @@ -package vimification.model.person; - -import static java.util.Objects.requireNonNull; -import static vimification.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's name in the address book. Guarantees: immutable; is valid as declared in - * {@link #isValidName(String)} - */ -public class Name { - - public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, otherwise " " (a blank string) - * becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; - - public final String fullName; - - /** - * Constructs a {@code Name}. - * - * @param name A valid name. - */ - public Name(String name) { - requireNonNull(name); - checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); - fullName = name; - } - - /** - * Returns true if a given string is a valid name. - */ - public static boolean isValidName(String test) { - return test.matches(VALIDATION_REGEX); - } - - - @Override - public String toString() { - return fullName; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check - } - - @Override - public int hashCode() { - return fullName.hashCode(); - } - -} diff --git a/src/main/java/vimification/model/person/Person.java b/src/main/java/vimification/model/person/Person.java deleted file mode 100644 index fc0c46d635a..00000000000 --- a/src/main/java/vimification/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package vimification.model.person; - -import static vimification.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import vimification.model.tag.Tag; - -/** - * Represents a Person in the address book. Guarantees: details are present and not null, field - * values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} if - * modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. This defines a weaker notion of equality - * between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. This defines a stronger - * notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/vimification/model/person/Phone.java b/src/main/java/vimification/model/person/Phone.java deleted file mode 100644 index 08c3a97a49b..00000000000 --- a/src/main/java/vimification/model/person/Phone.java +++ /dev/null @@ -1,53 +0,0 @@ -package vimification.model.person; - -import static java.util.Objects.requireNonNull; -import static vimification.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's phone number in the address book. Guarantees: immutable; is valid as - * declared in {@link #isValidPhone(String)} - */ -public class Phone { - - - public static final String MESSAGE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String VALIDATION_REGEX = "\\d{3,}"; - public final String value; - - /** - * Constructs a {@code Phone}. - * - * @param phone A valid phone number. - */ - public Phone(String phone) { - requireNonNull(phone); - checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS); - value = phone; - } - - /** - * Returns true if a given string is a valid phone number. - */ - public static boolean isValidPhone(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Phone // instanceof handles nulls - && value.equals(((Phone) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/vimification/model/person/UniquePersonList.java b/src/main/java/vimification/model/person/UniquePersonList.java deleted file mode 100644 index 6e861f16a2a..00000000000 --- a/src/main/java/vimification/model/person/UniquePersonList.java +++ /dev/null @@ -1,136 +0,0 @@ -package vimification.model.person; - -import static java.util.Objects.requireNonNull; -import static vimification.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import vimification.model.person.exceptions.DuplicatePersonException; -import vimification.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. A - * person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, - * adding and updating of persons uses Person#isSamePerson(Person) for equality so as to ensure that - * the person being added or updated is unique in terms of identity in the UniquePersonList. - * However, the removal of a person uses Person#equals(Object) so as to ensure that the person with - * exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - private final ObservableList internalUnmodifiableList = - FXCollections.unmodifiableObservableList(internalList); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. {@code target} must - * exist in the list. The person identity of {@code editedPerson} must not be the same as - * another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - - /** - * Replaces the contents of this list with {@code persons}. {@code persons} must not contain - * duplicate persons. - */ - public void setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * Returns the backing list as an unmodifiable {@code ObservableList}. - */ - public ObservableList asUnmodifiableObservableList() { - return internalUnmodifiableList; - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && internalList.equals(((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/vimification/model/person/exceptions/PersonNotFoundException.java b/src/main/java/vimification/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index 0b9c450b1ef..00000000000 --- a/src/main/java/vimification/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,7 +0,0 @@ -package vimification.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException { -} diff --git a/src/main/java/vimification/model/tag/Tag.java b/src/main/java/vimification/model/tag/Tag.java deleted file mode 100644 index 9789646dda2..00000000000 --- a/src/main/java/vimification/model/tag/Tag.java +++ /dev/null @@ -1,54 +0,0 @@ -package vimification.model.tag; - -import static java.util.Objects.requireNonNull; -import static vimification.commons.util.AppUtil.checkArgument; - -/** - * Represents a Tag in the address book. Guarantees: immutable; name is valid as declared in - * {@link #isValidTagName(String)} - */ -public class Tag { - - public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String VALIDATION_REGEX = "\\p{Alnum}+"; - - public final String tagName; - - /** - * Constructs a {@code Tag}. - * - * @param tagName A valid tag name. - */ - public Tag(String tagName) { - requireNonNull(tagName); - checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS); - this.tagName = tagName; - } - - /** - * Returns true if a given string is a valid tag name. - */ - public static boolean isValidTagName(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Tag // instanceof handles nulls - && tagName.equals(((Tag) other).tagName)); // state check - } - - @Override - public int hashCode() { - return tagName.hashCode(); - } - - /** - * Format state as text for viewing. - */ - public String toString() { - return '[' + tagName + ']'; - } - -} diff --git a/src/main/java/vimification/model/task/Date.java b/src/main/java/vimification/model/task/Date.java new file mode 100644 index 00000000000..4b1172f046e --- /dev/null +++ b/src/main/java/vimification/model/task/Date.java @@ -0,0 +1,50 @@ +package vimification.model.task; + +import static java.util.Objects.requireNonNull; +import static vimification.commons.util.AppUtil.checkArgument; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + + +public class Date { + + public static final String MESSAGE_CONSTRAINTS = ""; + public static final String VALIDATION_REGEX =""; + + public final LocalDate date; + public final String value; + private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MMM-dd-yyyy"); + + /** + * Constructs an {@code Date}. + * + * @param date A valid date. + */ + public Date(String date) { + requireNonNull(date); + checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS); + this.date = LocalDate.parse(date, dateFormatter); + value = dateFormatter.format(this.date); + } + + /** + * Returns if a given string is a valid email. + */ + public static boolean isValidDate(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Date // instanceof handles nulls + && date.equals(((Date) other).date)); // state check + } + +} diff --git a/src/main/java/vimification/model/task/Deadline.java b/src/main/java/vimification/model/task/Deadline.java new file mode 100644 index 00000000000..6e4c95b13bd --- /dev/null +++ b/src/main/java/vimification/model/task/Deadline.java @@ -0,0 +1,24 @@ +package vimification.model.task; + +public class Deadline extends Task { + + private final Date deadline; + + public Deadline(Description description, Status status, Date deadline) { + super(description, status, new Type("Deadline")); + this.deadline = deadline; + } + + public Date getDeadline() { return deadline; } + + + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getType()) + .append(" ") + .append(super.toString()) + .append("; by: ") + .append(getDeadline()); + return builder.toString(); + } +} diff --git a/src/main/java/vimification/model/task/Description.java b/src/main/java/vimification/model/task/Description.java new file mode 100644 index 00000000000..efad978cec1 --- /dev/null +++ b/src/main/java/vimification/model/task/Description.java @@ -0,0 +1,54 @@ +package vimification.model.task; + +import static java.util.Objects.requireNonNull; +import static vimification.commons.util.AppUtil.checkArgument; + +/** + * Represents a Person's name in the address book. + * Guarantees: immutable; is valid as declared in {@link #isValidDescription(String)} + */ +public class Description { + + public static final String MESSAGE_CONSTRAINTS = + ""; + + /* + * The first character of the address must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = ""; + + public final String description; + + /** + * Constructs a {@code Name}. + * + * @param description A valid name. + */ + public Description(String description) { + requireNonNull(description); + checkArgument(isValidDescription(description), MESSAGE_CONSTRAINTS); + this.description = description; + } + + /** + * Returns true if a given string is a valid name. + */ + public static boolean isValidDescription(String test) { + return test.matches(VALIDATION_REGEX); + } + + + @Override + public String toString() { + return description; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Description // instanceof handles nulls + && description.equals(((Description) other).description)); // state check + } + +} diff --git a/src/main/java/vimification/model/task/Event.java b/src/main/java/vimification/model/task/Event.java new file mode 100644 index 00000000000..8aefb554eb6 --- /dev/null +++ b/src/main/java/vimification/model/task/Event.java @@ -0,0 +1,32 @@ +package vimification.model.task; + +public class Event extends Task { + + private final Date startDate; + private final Date endDate; + + + public Event(Description description, Status status, Date startDate, Date endDate) { + super(description, status, new Type("Event")); + this.startDate = startDate; + this.endDate = endDate; + } + + public Date getStartDate() { return startDate; } + + public Date getEndDate() { + return endDate; + } + + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getType()) + .append(" ") + .append(super.toString()) + .append("; from: ") + .append(getStartDate()) + .append(" to: ") + .append(getEndDate()); + return builder.toString(); + } +} diff --git a/src/main/java/vimification/model/person/NameContainsKeywordsPredicate.java b/src/main/java/vimification/model/task/NameContainsKeywordsPredicate.java similarity index 67% rename from src/main/java/vimification/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/vimification/model/task/NameContainsKeywordsPredicate.java index de53f924724..c33b3119acf 100644 --- a/src/main/java/vimification/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/vimification/model/task/NameContainsKeywordsPredicate.java @@ -1,4 +1,4 @@ -package vimification.model.person; +package vimification.model.task; import java.util.List; import java.util.function.Predicate; @@ -8,7 +8,7 @@ /** * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. */ -public class NameContainsKeywordsPredicate implements Predicate { +public class NameContainsKeywordsPredicate implements Predicate { private final List keywords; public NameContainsKeywordsPredicate(List keywords) { @@ -16,18 +16,16 @@ public NameContainsKeywordsPredicate(List keywords) { } @Override - public boolean test(Person person) { + public boolean test(Task task) { return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, - keyword)); + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(task.getDescription().description, keyword)); } @Override public boolean equals(Object other) { return other == this // short circuit if same object || (other instanceof NameContainsKeywordsPredicate // instanceof handles nulls - && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state - // check + && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state check } } diff --git a/src/main/java/vimification/model/task/Status.java b/src/main/java/vimification/model/task/Status.java new file mode 100644 index 00000000000..35241a9a6ae --- /dev/null +++ b/src/main/java/vimification/model/task/Status.java @@ -0,0 +1,47 @@ +package vimification.model.task; + +import static java.util.Objects.requireNonNull; +import static vimification.commons.util.AppUtil.checkArgument; + + +public class Status { + + + public Boolean done; + public String value; + + + public Status(Boolean status) { + requireNonNull(status); + done = status; + value = Integer.toString((done) ? 1 : 0); + } + + public Status(String status) { + requireNonNull(status); + done = status.equals("1"); + value = status; + } + + public void setDone() { + done = true; + value = Integer.toString(1); + } + + public void setNotDone() { + done = false; + value = Integer.toString(0); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Status // instanceof handles nulls + && done == ((Status) other).done); // state check + } +} diff --git a/src/main/java/vimification/model/task/Task.java b/src/main/java/vimification/model/task/Task.java new file mode 100644 index 00000000000..8fdfb8c3c91 --- /dev/null +++ b/src/main/java/vimification/model/task/Task.java @@ -0,0 +1,85 @@ +package vimification.model.task; + +import static vimification.commons.util.CollectionUtil.requireAllNonNull; + + +/** + * Represents a Person in the address book. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public abstract class Task { + + private final Description description; + private final Status status; + private final Type type; + + /** + * Every field must be present and not null. + */ + public Task(Description description, Status status, Type type) { + requireAllNonNull(description, status); + this.description = description; + this.status = status; + this.type = type; + } + + public Description getDescription() { + return description; + } + + public Status getStatus() { + return status; + } + + public Type getType() { + return type; + } + + + public boolean isSameTask(Task otherTask) { + if (otherTask == this) { + return true; + } + + return otherTask != null + && otherTask.getDescription().equals(getDescription()); + } + + public void setDone() { + status.setDone(); + } + + public void setNotDone() { + status.setNotDone(); + } + + /** + * Returns true if both persons have the same identity and data fields. + * This defines a stronger notion of equality between two persons. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Task)) { + return false; + } + + Task otherTask = (Task) other; + return otherTask.getDescription().equals(getDescription()) + && otherTask.getStatus().equals(getStatus()); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("Description: ") + .append(getDescription()) + .append("; Status: ") + .append(getStatus()); + return builder.toString(); + } + +} diff --git a/src/main/java/vimification/model/task/Todo.java b/src/main/java/vimification/model/task/Todo.java new file mode 100644 index 00000000000..6a4b55b6b28 --- /dev/null +++ b/src/main/java/vimification/model/task/Todo.java @@ -0,0 +1,16 @@ +package vimification.model.task; + +public class Todo extends Task{ + + public Todo(Description description, Status status) { + super(description, status, new Type("Todo")); + } + + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getType()) + .append(" ") + .append(super.toString()); + return builder.toString(); + } +} diff --git a/src/main/java/vimification/model/task/Type.java b/src/main/java/vimification/model/task/Type.java new file mode 100644 index 00000000000..a2806ed38f5 --- /dev/null +++ b/src/main/java/vimification/model/task/Type.java @@ -0,0 +1,34 @@ +package vimification.model.task; + +import static java.util.Objects.requireNonNull; +import static vimification.commons.util.AppUtil.checkArgument; + +public class Type { + + public static final String MESSAGE_CONSTRAINTS = ""; + public static final String VALIDATION_REGEX = ""; + + public final String value; + + public Type(String type) { + requireNonNull(type); + checkArgument(isValidType(type), MESSAGE_CONSTRAINTS); + this.value = type; + } + + public static boolean isValidType(String test) { + return test.matches(VALIDATION_REGEX); + } + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Type // instanceof handles nulls + && value.equals(((Type) other).value)); // state check + } +} + diff --git a/src/main/java/vimification/model/task/UniqueTaskList.java b/src/main/java/vimification/model/task/UniqueTaskList.java new file mode 100644 index 00000000000..e0984629e31 --- /dev/null +++ b/src/main/java/vimification/model/task/UniqueTaskList.java @@ -0,0 +1,144 @@ +package vimification.model.task; + +import static java.util.Objects.requireNonNull; +import static vimification.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import vimification.model.task.exceptions.DuplicateTaskException; +import vimification.model.task.exceptions.TaskNotFoundException; + +/** + * A list of tasks that enforces uniqueness between its elements and does not allow nulls. + * A tasks is considered unique by comparing using {@code Task#isSameTask(Task)}. As such, adding and updating of + * tasks uses Task#isSameTask(Task) for equality so as to ensure that the task being added or updated is + * unique in terms of identity in the UniqueTaskList. However, the removal of a Task uses Task#equals(Object) so + * as to ensure that the Task with exactly the same fields will be removed. + * + * Supports a minimal set of list operations. + * + * @see Task#isSameTask(Task) + */ +public class UniqueTaskList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent person as the given argument. + */ + public boolean contains(Task toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameTask); + } + + /** + * Adds a person to the list. + * The person must not already exist in the list. + */ + public void add(Task toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateTaskException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the person {@code target} in the list with {@code editedPerson}. + * {@code target} must exist in the list. + * The person identity of {@code editedPerson} must not be the same as another existing person in the list. + */ + public void setTasks(Task target, Task editedTask) { + requireAllNonNull(target, editedTask); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new TaskNotFoundException(); + } + + if (!target.isSameTask(editedTask) && contains(editedTask)) { + throw new DuplicateTaskException(); + } + + internalList.set(index, editedTask); + } + + /** + * Removes the equivalent person from the list. + * The person must exist in the list. + */ + public void remove(Task toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new TaskNotFoundException(); + } + } + + public void mark(Task task) { + requireNonNull(task); + int index = internalList.indexOf(task); + internalList.get(index).setDone(); + } + + public void unmark(Task task) { + requireNonNull(task); + int index = internalList.indexOf(task); + internalList.get(index).setNotDone(); + } + + public void setTasks(UniqueTaskList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code persons}. + * {@code persons} must not contain duplicate persons. + */ + public void setTasks(List tasks) { + requireAllNonNull(tasks); + if (!tasksAreUnique(tasks)) { + throw new DuplicateTaskException(); + } + + internalList.setAll(tasks); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof UniqueTaskList // instanceof handles nulls + && internalList.equals(((UniqueTaskList) other).internalList)); + } + + /** + * Returns true if {@code persons} contains only unique persons. + */ + private boolean tasksAreUnique(List tasks) { + for (int i = 0; i < tasks.size() - 1; i++) { + for (int j = i + 1; j < tasks.size(); j++) { + if (tasks.get(i).isSameTask(tasks.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/vimification/model/person/exceptions/DuplicatePersonException.java b/src/main/java/vimification/model/task/exceptions/DuplicateTaskException.java similarity index 58% rename from src/main/java/vimification/model/person/exceptions/DuplicatePersonException.java rename to src/main/java/vimification/model/task/exceptions/DuplicateTaskException.java index 91c5e7dbd42..b30b93755a1 100644 --- a/src/main/java/vimification/model/person/exceptions/DuplicatePersonException.java +++ b/src/main/java/vimification/model/task/exceptions/DuplicateTaskException.java @@ -1,11 +1,11 @@ -package vimification.model.person.exceptions; +package vimification.model.task.exceptions; /** * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if * they have the same identity). */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { +public class DuplicateTaskException extends RuntimeException { + public DuplicateTaskException() { super("Operation would result in duplicate persons"); } } diff --git a/src/main/java/vimification/model/task/exceptions/TaskNotFoundException.java b/src/main/java/vimification/model/task/exceptions/TaskNotFoundException.java new file mode 100644 index 00000000000..b22257fccfe --- /dev/null +++ b/src/main/java/vimification/model/task/exceptions/TaskNotFoundException.java @@ -0,0 +1,7 @@ +package vimification.model.task.exceptions; + +/** + * Signals that the operation is unable to find the specified person. + */ +public class TaskNotFoundException extends RuntimeException { +} diff --git a/src/main/java/vimification/model/util/SampleDataUtil.java b/src/main/java/vimification/model/util/SampleDataUtil.java deleted file mode 100644 index 3959db5b144..00000000000 --- a/src/main/java/vimification/model/util/SampleDataUtil.java +++ /dev/null @@ -1,66 +0,0 @@ -package vimification.model.util; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -import vimification.model.AddressBook; -import vimification.model.ReadOnlyAddressBook; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Person; -import vimification.model.person.Phone; -import vimification.model.tag.Tag; - -/** - * Contains utility methods for populating {@code AddressBook} with sample data. - */ -public class SampleDataUtil { - 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"), - 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"), - 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"), - 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"), - 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"), - 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"), - getTagSet("colleagues")) - }; - } - - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); - } - return sampleAb; - } - - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - -} diff --git a/src/main/java/vimification/storage/AddressBookStorage.java b/src/main/java/vimification/storage/AddressBookStorage.java index 5af5286d1ae..6e9a4a40415 100644 --- a/src/main/java/vimification/storage/AddressBookStorage.java +++ b/src/main/java/vimification/storage/AddressBookStorage.java @@ -5,7 +5,7 @@ import java.util.Optional; import vimification.commons.exceptions.DataConversionException; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; /** * Represents a storage for {@link vimification.model.AddressBook}. @@ -18,31 +18,31 @@ public interface AddressBookStorage { Path getAddressBookFilePath(); /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. Returns {@code Optional.empty()} + * Returns AddressBook data as a {@link ReadOnlyTaskPlanner}. Returns {@code Optional.empty()} * if storage file is not found. * * @throws DataConversionException if the data in storage is not in the expected format. * @throws IOException if there was any problem when reading from the storage. */ - Optional readAddressBook() throws DataConversionException, IOException; + Optional readAddressBook() throws DataConversionException, IOException; /** * @see #getAddressBookFilePath() */ - Optional readAddressBook(Path filePath) + Optional readAddressBook(Path filePath) throws DataConversionException, IOException; /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. + * Saves the given {@link ReadOnlyTaskPlanner} to the storage. * * @param addressBook cannot be null. * @throws IOException if there was any problem writing to the file. */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + void saveAddressBook(ReadOnlyTaskPlanner addressBook) throws IOException; /** - * @see #saveAddressBook(ReadOnlyAddressBook) + * @see #saveAddressBook(ReadOnlyTaskPlanner) */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; + void saveAddressBook(ReadOnlyTaskPlanner addressBook, Path filePath) throws IOException; } diff --git a/src/main/java/vimification/storage/JsonAdaptedPerson.java b/src/main/java/vimification/storage/JsonAdaptedPerson.java index 8f479ef5f7d..570af16d32c 100644 --- a/src/main/java/vimification/storage/JsonAdaptedPerson.java +++ b/src/main/java/vimification/storage/JsonAdaptedPerson.java @@ -10,11 +10,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import vimification.commons.exceptions.IllegalValueException; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Person; -import vimification.model.person.Phone; import vimification.model.tag.Tag; /** diff --git a/src/main/java/vimification/storage/JsonAddressBookStorage.java b/src/main/java/vimification/storage/JsonAddressBookStorage.java index fef8959f3b0..bdc8d48bee4 100644 --- a/src/main/java/vimification/storage/JsonAddressBookStorage.java +++ b/src/main/java/vimification/storage/JsonAddressBookStorage.java @@ -12,7 +12,7 @@ import vimification.commons.exceptions.IllegalValueException; import vimification.commons.util.FileUtil; import vimification.commons.util.JsonUtil; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; /** * A class to access AddressBook data stored as a json file on the hard disk. @@ -32,7 +32,7 @@ public Path getAddressBookFilePath() { } @Override - public Optional readAddressBook() throws DataConversionException { + public Optional readAddressBook() throws DataConversionException { return readAddressBook(filePath); } @@ -42,7 +42,7 @@ public Optional readAddressBook() throws DataConversionExce * @param filePath location of the data. Cannot be null. * @throws DataConversionException if the file is not in the correct format. */ - public Optional readAddressBook(Path filePath) + public Optional readAddressBook(Path filePath) throws DataConversionException { requireNonNull(filePath); @@ -61,16 +61,16 @@ public Optional readAddressBook(Path filePath) } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { + public void saveAddressBook(ReadOnlyTaskPlanner addressBook) throws IOException { saveAddressBook(addressBook, filePath); } /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. + * Similar to {@link #saveAddressBook(ReadOnlyTaskPlanner)}. * * @param filePath location of the data. Cannot be null. */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { + public void saveAddressBook(ReadOnlyTaskPlanner addressBook, Path filePath) throws IOException { requireNonNull(addressBook); requireNonNull(filePath); diff --git a/src/main/java/vimification/storage/JsonSerializableAddressBook.java b/src/main/java/vimification/storage/JsonSerializableAddressBook.java index ffc257b8c13..f1feea831bf 100644 --- a/src/main/java/vimification/storage/JsonSerializableAddressBook.java +++ b/src/main/java/vimification/storage/JsonSerializableAddressBook.java @@ -10,8 +10,7 @@ import vimification.commons.exceptions.IllegalValueException; import vimification.model.AddressBook; -import vimification.model.ReadOnlyAddressBook; -import vimification.model.person.Person; +import vimification.model.ReadOnlyTaskPlanner; /** * An Immutable AddressBook that is serializable to JSON format. @@ -33,12 +32,12 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List readAddressBook() throws DataConversionException, IOException; + Optional readAddressBook() throws DataConversionException, IOException; @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + void saveAddressBook(ReadOnlyTaskPlanner addressBook) throws IOException; } diff --git a/src/main/java/vimification/storage/StorageManager.java b/src/main/java/vimification/storage/StorageManager.java index dafae72ae6c..d5b91da8727 100644 --- a/src/main/java/vimification/storage/StorageManager.java +++ b/src/main/java/vimification/storage/StorageManager.java @@ -7,7 +7,7 @@ import vimification.commons.core.LogsCenter; import vimification.commons.exceptions.DataConversionException; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; import vimification.model.ReadOnlyUserPrefs; import vimification.model.UserPrefs; @@ -56,25 +56,25 @@ public Path getAddressBookFilePath() { } @Override - public Optional readAddressBook() + public Optional readAddressBook() throws DataConversionException, IOException { return readAddressBook(addressBookStorage.getAddressBookFilePath()); } @Override - public Optional readAddressBook(Path filePath) + public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { logger.fine("Attempting to read data from file: " + filePath); return addressBookStorage.readAddressBook(filePath); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { + public void saveAddressBook(ReadOnlyTaskPlanner addressBook) throws IOException { saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { + public void saveAddressBook(ReadOnlyTaskPlanner addressBook, Path filePath) throws IOException { logger.fine("Attempting to write to data file: " + filePath); addressBookStorage.saveAddressBook(addressBook, filePath); } diff --git a/src/main/java/vimification/ui/PersonCard.java b/src/main/java/vimification/ui/PersonCard.java index cb05d3b2b3b..9bc113166c4 100644 --- a/src/main/java/vimification/ui/PersonCard.java +++ b/src/main/java/vimification/ui/PersonCard.java @@ -7,7 +7,6 @@ import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; -import vimification.model.person.Person; /** * An UI component that displays information of a {@code Person}. diff --git a/src/main/java/vimification/ui/PersonListPanel.java b/src/main/java/vimification/ui/PersonListPanel.java index 61dc9d353bf..202dc889d13 100644 --- a/src/main/java/vimification/ui/PersonListPanel.java +++ b/src/main/java/vimification/ui/PersonListPanel.java @@ -8,7 +8,6 @@ import javafx.scene.control.ListView; import javafx.scene.layout.Region; import vimification.commons.core.LogsCenter; -import vimification.model.person.Person; /** * Panel containing the list of persons. diff --git a/src/test/java/vimification/logic/LogicManagerTest.java b/src/test/java/vimification/logic/LogicManagerTest.java index 048dbd496f8..07bdc1d693d 100644 --- a/src/test/java/vimification/logic/LogicManagerTest.java +++ b/src/test/java/vimification/logic/LogicManagerTest.java @@ -24,9 +24,8 @@ import vimification.logic.parser.ParseException; import vimification.model.Model; import vimification.model.ModelManager; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; import vimification.model.UserPrefs; -import vimification.model.person.Person; import vimification.storage.JsonAddressBookStorage; import vimification.storage.JsonUserPrefsStorage; import vimification.storage.StorageManager; @@ -167,7 +166,7 @@ private JsonAddressBookIoExceptionThrowingStub(Path filePath) { } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) + public void saveAddressBook(ReadOnlyTaskPlanner addressBook, Path filePath) throws IOException { throw DUMMY_IO_EXCEPTION; } diff --git a/src/test/java/vimification/logic/commands/AddCommandIntegrationTest.java b/src/test/java/vimification/logic/commands/AddCommandIntegrationTest.java index 60ac73a9439..d09f29df71e 100644 --- a/src/test/java/vimification/logic/commands/AddCommandIntegrationTest.java +++ b/src/test/java/vimification/logic/commands/AddCommandIntegrationTest.java @@ -10,7 +10,6 @@ import vimification.model.Model; import vimification.model.ModelManager; import vimification.model.UserPrefs; -import vimification.model.person.Person; import vimification.testutil.PersonBuilder; /** diff --git a/src/test/java/vimification/logic/commands/AddCommandTest.java b/src/test/java/vimification/logic/commands/AddCommandTest.java index 9fab0ff53f4..d132edf1b50 100644 --- a/src/test/java/vimification/logic/commands/AddCommandTest.java +++ b/src/test/java/vimification/logic/commands/AddCommandTest.java @@ -18,9 +18,8 @@ import vimification.logic.commands.exceptions.CommandException; import vimification.model.AddressBook; import vimification.model.Model; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; import vimification.model.ReadOnlyUserPrefs; -import vimification.model.person.Person; import vimification.testutil.PersonBuilder; public class AddCommandTest { @@ -116,12 +115,12 @@ public void addPerson(Person person) { } @Override - public void setAddressBook(ReadOnlyAddressBook newData) { + public void setAddressBook(ReadOnlyTaskPlanner newData) { throw new AssertionError("This method should not be called."); } @Override - public ReadOnlyAddressBook getAddressBook() { + public ReadOnlyTaskPlanner getAddressBook() { throw new AssertionError("This method should not be called."); } @@ -188,7 +187,7 @@ public void addPerson(Person person) { } @Override - public ReadOnlyAddressBook getAddressBook() { + public ReadOnlyTaskPlanner getAddressBook() { return new AddressBook(); } } diff --git a/src/test/java/vimification/logic/commands/CommandTestUtil.java b/src/test/java/vimification/logic/commands/CommandTestUtil.java index 934ea92c67f..efea59908c5 100644 --- a/src/test/java/vimification/logic/commands/CommandTestUtil.java +++ b/src/test/java/vimification/logic/commands/CommandTestUtil.java @@ -17,8 +17,7 @@ import vimification.logic.commands.exceptions.CommandException; import vimification.model.AddressBook; import vimification.model.Model; -import vimification.model.person.NameContainsKeywordsPredicate; -import vimification.model.person.Person; +import vimification.model.task.NameContainsKeywordsPredicate; import vimification.testutil.EditPersonDescriptorBuilder; /** diff --git a/src/test/java/vimification/logic/commands/DeleteCommandTest.java b/src/test/java/vimification/logic/commands/DeleteCommandTest.java index e7be276818e..ee62a7a0d71 100644 --- a/src/test/java/vimification/logic/commands/DeleteCommandTest.java +++ b/src/test/java/vimification/logic/commands/DeleteCommandTest.java @@ -16,7 +16,6 @@ import vimification.model.Model; import vimification.model.ModelManager; import vimification.model.UserPrefs; -import vimification.model.person.Person; /** * Contains integration tests (interaction with the Model) and unit tests for {@code DeleteCommand}. diff --git a/src/test/java/vimification/logic/commands/EditCommandTest.java b/src/test/java/vimification/logic/commands/EditCommandTest.java index 29a4109f8aa..82103deb735 100644 --- a/src/test/java/vimification/logic/commands/EditCommandTest.java +++ b/src/test/java/vimification/logic/commands/EditCommandTest.java @@ -23,7 +23,6 @@ import vimification.model.Model; import vimification.model.ModelManager; import vimification.model.UserPrefs; -import vimification.model.person.Person; import vimification.testutil.EditPersonDescriptorBuilder; import vimification.testutil.PersonBuilder; diff --git a/src/test/java/vimification/logic/commands/FindCommandTest.java b/src/test/java/vimification/logic/commands/FindCommandTest.java index 199eaae3f38..7060ec8122e 100644 --- a/src/test/java/vimification/logic/commands/FindCommandTest.java +++ b/src/test/java/vimification/logic/commands/FindCommandTest.java @@ -18,7 +18,7 @@ import vimification.model.Model; import vimification.model.ModelManager; import vimification.model.UserPrefs; -import vimification.model.person.NameContainsKeywordsPredicate; +import vimification.model.task.NameContainsKeywordsPredicate; /** * Contains integration tests (interaction with the Model) for {@code FindCommand}. diff --git a/src/test/java/vimification/logic/parser/AddCommandParserTest.java b/src/test/java/vimification/logic/parser/AddCommandParserTest.java index 94c6d5f9a87..806051e329f 100644 --- a/src/test/java/vimification/logic/parser/AddCommandParserTest.java +++ b/src/test/java/vimification/logic/parser/AddCommandParserTest.java @@ -32,11 +32,6 @@ import org.junit.jupiter.api.Test; import vimification.logic.commands.AddCommand; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Person; -import vimification.model.person.Phone; import vimification.model.tag.Tag; import vimification.testutil.PersonBuilder; diff --git a/src/test/java/vimification/logic/parser/AddressBookParserTest.java b/src/test/java/vimification/logic/parser/AddressBookParserTest.java index 596e4cb5270..d13236d8150 100644 --- a/src/test/java/vimification/logic/parser/AddressBookParserTest.java +++ b/src/test/java/vimification/logic/parser/AddressBookParserTest.java @@ -22,8 +22,7 @@ import vimification.logic.commands.FindCommand; import vimification.logic.commands.HelpCommand; import vimification.logic.commands.ListCommand; -import vimification.model.person.NameContainsKeywordsPredicate; -import vimification.model.person.Person; +import vimification.model.task.NameContainsKeywordsPredicate; import vimification.testutil.EditPersonDescriptorBuilder; import vimification.testutil.PersonBuilder; import vimification.testutil.PersonUtil; diff --git a/src/test/java/vimification/logic/parser/EditCommandParserTest.java b/src/test/java/vimification/logic/parser/EditCommandParserTest.java index 4f86ce99f78..6b9b515d718 100644 --- a/src/test/java/vimification/logic/parser/EditCommandParserTest.java +++ b/src/test/java/vimification/logic/parser/EditCommandParserTest.java @@ -36,10 +36,6 @@ import vimification.commons.core.index.Index; import vimification.logic.commands.EditCommand; import vimification.logic.commands.EditCommand.EditPersonDescriptor; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Phone; import vimification.model.tag.Tag; import vimification.testutil.EditPersonDescriptorBuilder; diff --git a/src/test/java/vimification/logic/parser/FindCommandParserTest.java b/src/test/java/vimification/logic/parser/FindCommandParserTest.java index 75ba2e61f26..8fb35046abf 100644 --- a/src/test/java/vimification/logic/parser/FindCommandParserTest.java +++ b/src/test/java/vimification/logic/parser/FindCommandParserTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test; import vimification.logic.commands.FindCommand; -import vimification.model.person.NameContainsKeywordsPredicate; +import vimification.model.task.NameContainsKeywordsPredicate; public class FindCommandParserTest { diff --git a/src/test/java/vimification/logic/parser/ParserUtilTest.java b/src/test/java/vimification/logic/parser/ParserUtilTest.java index 2db8d8fe70e..44a67a19f82 100644 --- a/src/test/java/vimification/logic/parser/ParserUtilTest.java +++ b/src/test/java/vimification/logic/parser/ParserUtilTest.java @@ -13,10 +13,6 @@ import org.junit.jupiter.api.Test; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Phone; import vimification.model.tag.Tag; public class ParserUtilTest { diff --git a/src/test/java/vimification/model/AddressBookTest.java b/src/test/java/vimification/model/AddressBookTest.java index c9e6d258200..f5d58b4f4a2 100644 --- a/src/test/java/vimification/model/AddressBookTest.java +++ b/src/test/java/vimification/model/AddressBookTest.java @@ -18,8 +18,7 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import vimification.model.person.Person; -import vimification.model.person.exceptions.DuplicatePersonException; +import vimification.model.task.exceptions.DuplicateTaskException; import vimification.testutil.PersonBuilder; public class AddressBookTest { @@ -52,7 +51,7 @@ public void resetData_withDuplicatePersons_throwsDuplicatePersonException() { List newPersons = Arrays.asList(ALICE, editedAlice); AddressBookStub newData = new AddressBookStub(newPersons); - assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData)); + assertThrows(DuplicateTaskException.class, () -> addressBook.resetData(newData)); } @Test @@ -87,9 +86,9 @@ public void getPersonList_modifyList_throwsUnsupportedOperationException() { } /** - * A stub ReadOnlyAddressBook whose persons list can violate interface constraints. + * A stub ReadOnlyTaskPlanner whose persons list can violate interface constraints. */ - private static class AddressBookStub implements ReadOnlyAddressBook { + private static class AddressBookStub implements ReadOnlyTaskPlanner { private final ObservableList persons = FXCollections.observableArrayList(); AddressBookStub(Collection persons) { diff --git a/src/test/java/vimification/model/ModelManagerTest.java b/src/test/java/vimification/model/ModelManagerTest.java index c537ecdfae6..f9c68a1d56b 100644 --- a/src/test/java/vimification/model/ModelManagerTest.java +++ b/src/test/java/vimification/model/ModelManagerTest.java @@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test; import vimification.commons.core.GuiSettings; -import vimification.model.person.NameContainsKeywordsPredicate; +import vimification.model.task.NameContainsKeywordsPredicate; import vimification.testutil.AddressBookBuilder; public class ModelManagerTest { diff --git a/src/test/java/vimification/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/vimification/model/person/NameContainsKeywordsPredicateTest.java index 3326d617710..86673f0387f 100644 --- a/src/test/java/vimification/model/person/NameContainsKeywordsPredicateTest.java +++ b/src/test/java/vimification/model/person/NameContainsKeywordsPredicateTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test; +import vimification.model.task.NameContainsKeywordsPredicate; import vimification.testutil.PersonBuilder; public class NameContainsKeywordsPredicateTest { diff --git a/src/test/java/vimification/model/person/UniquePersonListTest.java b/src/test/java/vimification/model/person/UniquePersonListTest.java index 9fa4fa228db..f22957cc99f 100644 --- a/src/test/java/vimification/model/person/UniquePersonListTest.java +++ b/src/test/java/vimification/model/person/UniquePersonListTest.java @@ -15,8 +15,9 @@ import org.junit.jupiter.api.Test; -import vimification.model.person.exceptions.DuplicatePersonException; -import vimification.model.person.exceptions.PersonNotFoundException; +import vimification.model.task.UniquePersonList; +import vimification.model.task.exceptions.DuplicateTaskException; +import vimification.model.task.exceptions.TaskNotFoundException; import vimification.testutil.PersonBuilder; public class UniquePersonListTest { @@ -56,7 +57,7 @@ public void add_nullPerson_throwsNullPointerException() { @Test public void add_duplicatePerson_throwsDuplicatePersonException() { uniquePersonList.add(ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.add(ALICE)); + assertThrows(DuplicateTaskException.class, () -> uniquePersonList.add(ALICE)); } @Test @@ -71,7 +72,7 @@ public void setPerson_nullEditedPerson_throwsNullPointerException() { @Test public void setPerson_targetPersonNotInList_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE)); + assertThrows(TaskNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE)); } @Test @@ -108,7 +109,7 @@ public void setPerson_editedPersonHasDifferentIdentity_success() { public void setPerson_editedPersonHasNonUniqueIdentity_throwsDuplicatePersonException() { uniquePersonList.add(ALICE); uniquePersonList.add(BOB); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPerson(ALICE, BOB)); + assertThrows(DuplicateTaskException.class, () -> uniquePersonList.setPerson(ALICE, BOB)); } @Test @@ -118,7 +119,7 @@ public void remove_nullPerson_throwsNullPointerException() { @Test public void remove_personDoesNotExist_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.remove(ALICE)); + assertThrows(TaskNotFoundException.class, () -> uniquePersonList.remove(ALICE)); } @Test @@ -163,7 +164,7 @@ public void setPersons_list_replacesOwnListWithProvidedList() { @Test public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() { List listWithDuplicatePersons = Arrays.asList(ALICE, ALICE); - assertThrows(DuplicatePersonException.class, + assertThrows(DuplicateTaskException.class, () -> uniquePersonList.setPersons(listWithDuplicatePersons)); } diff --git a/src/test/java/vimification/storage/JsonAdaptedPersonTest.java b/src/test/java/vimification/storage/JsonAdaptedPersonTest.java index 80ea44709a0..bbc9ab8b0d6 100644 --- a/src/test/java/vimification/storage/JsonAdaptedPersonTest.java +++ b/src/test/java/vimification/storage/JsonAdaptedPersonTest.java @@ -12,10 +12,6 @@ import org.junit.jupiter.api.Test; import vimification.commons.exceptions.IllegalValueException; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Phone; public class JsonAdaptedPersonTest { private static final String INVALID_NAME = "R@chel"; diff --git a/src/test/java/vimification/storage/JsonAddressBookStorageTest.java b/src/test/java/vimification/storage/JsonAddressBookStorageTest.java index 28addc6a95a..06077f18504 100644 --- a/src/test/java/vimification/storage/JsonAddressBookStorageTest.java +++ b/src/test/java/vimification/storage/JsonAddressBookStorageTest.java @@ -17,7 +17,7 @@ import vimification.commons.exceptions.DataConversionException; import vimification.model.AddressBook; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; public class JsonAddressBookStorageTest { private static final Path TEST_DATA_FOLDER = @@ -31,7 +31,7 @@ public void readAddressBook_nullFilePath_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> readAddressBook(null)); } - private java.util.Optional readAddressBook(String filePath) + private java.util.Optional readAddressBook(String filePath) throws Exception { return new JsonAddressBookStorage(Paths.get(filePath)) .readAddressBook(addToTestDataPathIfNotNull(filePath)); @@ -74,7 +74,7 @@ public void readAndSaveAddressBook_allInOrder_success() throws Exception { // Save in new file and read back jsonAddressBookStorage.saveAddressBook(original, filePath); - ReadOnlyAddressBook readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); + ReadOnlyTaskPlanner readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); assertEquals(original, new AddressBook(readBack)); // Modify data, overwrite exiting file, and read back @@ -100,7 +100,7 @@ public void saveAddressBook_nullAddressBook_throwsNullPointerException() { /** * Saves {@code addressBook} at the specified {@code filePath}. */ - private void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) { + private void saveAddressBook(ReadOnlyTaskPlanner addressBook, String filePath) { try { new JsonAddressBookStorage(Paths.get(filePath)) .saveAddressBook(addressBook, addToTestDataPathIfNotNull(filePath)); diff --git a/src/test/java/vimification/storage/StorageManagerTest.java b/src/test/java/vimification/storage/StorageManagerTest.java index e80634c0e9b..371fcea4723 100644 --- a/src/test/java/vimification/storage/StorageManagerTest.java +++ b/src/test/java/vimification/storage/StorageManagerTest.java @@ -12,7 +12,7 @@ import vimification.commons.core.GuiSettings; import vimification.model.AddressBook; -import vimification.model.ReadOnlyAddressBook; +import vimification.model.ReadOnlyTaskPlanner; import vimification.model.UserPrefs; public class StorageManagerTest { @@ -57,7 +57,7 @@ public void addressBookReadSave() throws Exception { */ AddressBook original = getTypicalAddressBook(); storageManager.saveAddressBook(original); - ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); + ReadOnlyTaskPlanner retrieved = storageManager.readAddressBook().get(); assertEquals(original, new AddressBook(retrieved)); } diff --git a/src/test/java/vimification/testutil/AddressBookBuilder.java b/src/test/java/vimification/testutil/AddressBookBuilder.java index d0d00e992ac..28e9a811eef 100644 --- a/src/test/java/vimification/testutil/AddressBookBuilder.java +++ b/src/test/java/vimification/testutil/AddressBookBuilder.java @@ -1,7 +1,6 @@ package vimification.testutil; import vimification.model.AddressBook; -import vimification.model.person.Person; /** * A utility class to help with building Addressbook objects. Example usage:
diff --git a/src/test/java/vimification/testutil/EditPersonDescriptorBuilder.java b/src/test/java/vimification/testutil/EditPersonDescriptorBuilder.java index 8ec923a537c..2a8923c1ef6 100644 --- a/src/test/java/vimification/testutil/EditPersonDescriptorBuilder.java +++ b/src/test/java/vimification/testutil/EditPersonDescriptorBuilder.java @@ -5,11 +5,6 @@ import java.util.stream.Stream; import vimification.logic.commands.EditCommand.EditPersonDescriptor; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Person; -import vimification.model.person.Phone; import vimification.model.tag.Tag; /** diff --git a/src/test/java/vimification/testutil/PersonBuilder.java b/src/test/java/vimification/testutil/PersonBuilder.java index f89021f0559..60657e35079 100644 --- a/src/test/java/vimification/testutil/PersonBuilder.java +++ b/src/test/java/vimification/testutil/PersonBuilder.java @@ -3,11 +3,6 @@ import java.util.HashSet; import java.util.Set; -import vimification.model.person.Address; -import vimification.model.person.Email; -import vimification.model.person.Name; -import vimification.model.person.Person; -import vimification.model.person.Phone; import vimification.model.tag.Tag; import vimification.model.util.SampleDataUtil; diff --git a/src/test/java/vimification/testutil/PersonUtil.java b/src/test/java/vimification/testutil/PersonUtil.java index a22ce41455e..d73285d3ba4 100644 --- a/src/test/java/vimification/testutil/PersonUtil.java +++ b/src/test/java/vimification/testutil/PersonUtil.java @@ -10,7 +10,6 @@ import vimification.logic.commands.AddCommand; import vimification.logic.commands.EditCommand.EditPersonDescriptor; -import vimification.model.person.Person; import vimification.model.tag.Tag; /** diff --git a/src/test/java/vimification/testutil/TestUtil.java b/src/test/java/vimification/testutil/TestUtil.java index 5c0db1b216d..28ec7acd9a8 100644 --- a/src/test/java/vimification/testutil/TestUtil.java +++ b/src/test/java/vimification/testutil/TestUtil.java @@ -7,7 +7,6 @@ import vimification.commons.core.index.Index; import vimification.model.Model; -import vimification.model.person.Person; /** * A utility class for test cases. diff --git a/src/test/java/vimification/testutil/TypicalPersons.java b/src/test/java/vimification/testutil/TypicalPersons.java index 2e3b2e477c3..28ea560df97 100644 --- a/src/test/java/vimification/testutil/TypicalPersons.java +++ b/src/test/java/vimification/testutil/TypicalPersons.java @@ -16,7 +16,6 @@ import java.util.List; import vimification.model.AddressBook; -import vimification.model.person.Person; /** * A utility class containing a list of {@code Person} objects to be used in tests. From 2f664ef09f3b9c601125bcc95474c60abc9d4e09 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 14 Mar 2023 18:01:25 +0800 Subject: [PATCH 022/470] Implement KeyEvent Listener on modularized MainWindow --- src/main/java/vimification/Gui.java | 1 + src/main/java/vimification/taskui/MainWindow.java | 6 ++++++ src/main/resources/view/Main.fxml | 8 ++++---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/vimification/Gui.java b/src/main/java/vimification/Gui.java index 626e1352e20..2fdf9e86871 100644 --- a/src/main/java/vimification/Gui.java +++ b/src/main/java/vimification/Gui.java @@ -16,6 +16,7 @@ public void start(Stage primaryStage) throws Exception { // Set window // configureStage(primaryStage); Scene scene = new Scene(mainWindow); + scene.getRoot().requestFocus(); primaryStage.setScene(scene); // Setting the stage to show our screen primaryStage.show(); // Render the stage. diff --git a/src/main/java/vimification/taskui/MainWindow.java b/src/main/java/vimification/taskui/MainWindow.java index 43107ee0bdf..43578e02ef3 100644 --- a/src/main/java/vimification/taskui/MainWindow.java +++ b/src/main/java/vimification/taskui/MainWindow.java @@ -8,6 +8,7 @@ import javafx.fxml.FXMLLoader; import javafx.scene.control.TextField; import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.VBox; // import javafx.scene.control.MenuItem; // import javafx.scene.control.TextInputControl; @@ -92,6 +93,11 @@ public Stage getPrimaryStage() { return primaryStage; } + @FXML + private void handleKeyPressed(KeyEvent ke) { + System.out.println("Key Pressed: " + ke.getCode()); + } + @FXML public void initialize() { textBoxComponent.setVisible(false); diff --git a/src/main/resources/view/Main.fxml b/src/main/resources/view/Main.fxml index 3c8e092f537..7cf4f5f4362 100644 --- a/src/main/resources/view/Main.fxml +++ b/src/main/resources/view/Main.fxml @@ -6,17 +6,17 @@ - + From d0cfcf719dfb526689997472ec4e2f0a4f651ffa Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 14 Mar 2023 20:55:28 +0800 Subject: [PATCH 023/470] Fix KeyEvent listener not working on MainWindow --- src/main/java/vimification/Gui.java | 1 + .../java/vimification/taskui/MainWindow.java | 21 +++++++++++-------- src/main/resources/view/Main.fxml | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/vimification/Gui.java b/src/main/java/vimification/Gui.java index 2fdf9e86871..f05ac77061d 100644 --- a/src/main/java/vimification/Gui.java +++ b/src/main/java/vimification/Gui.java @@ -12,6 +12,7 @@ public class Gui extends Application { public void start(Stage primaryStage) throws Exception { // Create control MainWindow mainWindow = new MainWindow(); + mainWindow.setFocusTraversable(true); // Important // Set window // configureStage(primaryStage); diff --git a/src/main/java/vimification/taskui/MainWindow.java b/src/main/java/vimification/taskui/MainWindow.java index 43578e02ef3..3698cae6b82 100644 --- a/src/main/java/vimification/taskui/MainWindow.java +++ b/src/main/java/vimification/taskui/MainWindow.java @@ -2,7 +2,6 @@ import java.io.IOException; -import javafx.application.Application; import javafx.application.Platform; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; @@ -53,13 +52,9 @@ public MainWindow() { fxmlLoader.setController(this); fxmlLoader.setRoot(this); fxmlLoader.load(); + setFocusTraversable(true); // Important - this.setOnKeyPressed(event -> { - if (event.getText().equals(":")) { - textBoxComponent.setVisible(true); - textBox.requestFocus(); - } - }); + // keyListener(); textBoxComponent.setOnKeyPressed(event -> { if (event.getCode().equals(KeyCode.ESCAPE)) { @@ -93,9 +88,17 @@ public Stage getPrimaryStage() { return primaryStage; } + /** + * Listener for handling all keyboard events to Vimification. + * + * @param event + */ @FXML - private void handleKeyPressed(KeyEvent ke) { - System.out.println("Key Pressed: " + ke.getCode()); + private void handleKeyPressed(KeyEvent event) { + if (event.getText().equals(":")) { + textBoxComponent.setVisible(true); + textBox.requestFocus(); + } } @FXML diff --git a/src/main/resources/view/Main.fxml b/src/main/resources/view/Main.fxml index 7cf4f5f4362..c0efdc65303 100644 --- a/src/main/resources/view/Main.fxml +++ b/src/main/resources/view/Main.fxml @@ -7,7 +7,7 @@ -