diff --git a/.gitignore b/.gitignore
index 284c4ca7cd9..fb27c423e76 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,9 +15,6 @@ src/main/resources/docs/
/*.log.*
hs_err_pid[0-9]*.log
-# Test sandbox files
-src/test/data/sandbox/
-
# MacOS custom attributes files created by Finder
.DS_Store
docs/_site/
diff --git a/README.md b/README.md
index 13f5c77403f..ce202e52372 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,16 @@
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
+[![CI Status](https://github.com/AY2324S1-CS2103T-T11-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2324S1-CS2103T-T11-4/tp/actions)
-![Ui](docs/images/Ui.png)
+![Ui](docs/images/MainUi.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.
+* This is **a vocabulary memorizing project for all language learners**.
+* This project is designed to assist language learners in memorizing words efficiently using a scientific approach based on the **Ebbinghaus Forgetting Curve**.
+ * The app provides a command-line interface (CLI) for users to interact with.
+ * It schedules word revision sessions at specific times to maximize word retention.
+* The project is based on a 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)**.
-* 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.
+* It is named `Flashlingo` because it was initially inspired by **flash cards** and **duolingo**.
+* For the detailed documentation of this project, see the **[Flashlingo Product Website](https://ay2324s1-cs2103t-t11-4.github.io/tp/)**.
+
+## Acknowledgments
+This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
diff --git a/src/main/resources/images/address_book_32.png b/bin/main/images/address_book_32.png
similarity index 100%
rename from src/main/resources/images/address_book_32.png
rename to bin/main/images/address_book_32.png
diff --git a/src/main/resources/images/calendar.png b/bin/main/images/calendar.png
similarity index 100%
rename from src/main/resources/images/calendar.png
rename to bin/main/images/calendar.png
diff --git a/src/main/resources/images/clock.png b/bin/main/images/clock.png
similarity index 100%
rename from src/main/resources/images/clock.png
rename to bin/main/images/clock.png
diff --git a/src/main/resources/images/fail.png b/bin/main/images/fail.png
similarity index 100%
rename from src/main/resources/images/fail.png
rename to bin/main/images/fail.png
diff --git a/bin/main/images/flash_lingo.png b/bin/main/images/flash_lingo.png
new file mode 100644
index 00000000000..aa2ff6ccabd
Binary files /dev/null and b/bin/main/images/flash_lingo.png differ
diff --git a/bin/main/images/help_icon.png b/bin/main/images/help_icon.png
new file mode 100644
index 00000000000..f8e80d6c1c5
Binary files /dev/null and b/bin/main/images/help_icon.png differ
diff --git a/src/main/resources/images/info_icon.png b/bin/main/images/info_icon.png
similarity index 100%
rename from src/main/resources/images/info_icon.png
rename to bin/main/images/info_icon.png
diff --git a/bin/main/seedu/flashlingo/AppParameters.class b/bin/main/seedu/flashlingo/AppParameters.class
new file mode 100644
index 00000000000..ce67cf6ba2c
Binary files /dev/null and b/bin/main/seedu/flashlingo/AppParameters.class differ
diff --git a/bin/main/seedu/flashlingo/Main.class b/bin/main/seedu/flashlingo/Main.class
new file mode 100644
index 00000000000..cd18e3eaa2e
Binary files /dev/null and b/bin/main/seedu/flashlingo/Main.class differ
diff --git a/bin/main/seedu/flashlingo/MainApp.class b/bin/main/seedu/flashlingo/MainApp.class
new file mode 100644
index 00000000000..f39ad39c3cc
Binary files /dev/null and b/bin/main/seedu/flashlingo/MainApp.class differ
diff --git a/bin/main/seedu/flashlingo/commons/core/Config.class b/bin/main/seedu/flashlingo/commons/core/Config.class
new file mode 100644
index 00000000000..0947f1b26d1
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/core/Config.class differ
diff --git a/bin/main/seedu/flashlingo/commons/core/GuiSettings.class b/bin/main/seedu/flashlingo/commons/core/GuiSettings.class
new file mode 100644
index 00000000000..d5053147387
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/core/GuiSettings.class differ
diff --git a/bin/main/seedu/flashlingo/commons/core/LogsCenter.class b/bin/main/seedu/flashlingo/commons/core/LogsCenter.class
new file mode 100644
index 00000000000..3a4e8745552
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/core/LogsCenter.class differ
diff --git a/bin/main/seedu/flashlingo/commons/core/Version.class b/bin/main/seedu/flashlingo/commons/core/Version.class
new file mode 100644
index 00000000000..6ff7f68361c
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/core/Version.class differ
diff --git a/bin/main/seedu/flashlingo/commons/core/index/Index.class b/bin/main/seedu/flashlingo/commons/core/index/Index.class
new file mode 100644
index 00000000000..10c05f9586b
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/core/index/Index.class differ
diff --git a/bin/main/seedu/flashlingo/commons/exceptions/DataLoadingException.class b/bin/main/seedu/flashlingo/commons/exceptions/DataLoadingException.class
new file mode 100644
index 00000000000..3d1c3b615f7
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/exceptions/DataLoadingException.class differ
diff --git a/bin/main/seedu/flashlingo/commons/exceptions/IllegalValueException.class b/bin/main/seedu/flashlingo/commons/exceptions/IllegalValueException.class
new file mode 100644
index 00000000000..bcfffc0e258
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/exceptions/IllegalValueException.class differ
diff --git a/bin/main/seedu/flashlingo/commons/util/AppUtil.class b/bin/main/seedu/flashlingo/commons/util/AppUtil.class
new file mode 100644
index 00000000000..686e53ceb46
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/util/AppUtil.class differ
diff --git a/bin/main/seedu/flashlingo/commons/util/CollectionUtil.class b/bin/main/seedu/flashlingo/commons/util/CollectionUtil.class
new file mode 100644
index 00000000000..52d22935cb3
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/util/CollectionUtil.class differ
diff --git a/bin/main/seedu/flashlingo/commons/util/ConfigUtil.class b/bin/main/seedu/flashlingo/commons/util/ConfigUtil.class
new file mode 100644
index 00000000000..33ce37d9935
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/util/ConfigUtil.class differ
diff --git a/bin/main/seedu/flashlingo/commons/util/FileUtil.class b/bin/main/seedu/flashlingo/commons/util/FileUtil.class
new file mode 100644
index 00000000000..280d2e05453
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/util/FileUtil.class differ
diff --git a/bin/main/seedu/flashlingo/commons/util/JsonUtil$LevelDeserializer.class b/bin/main/seedu/flashlingo/commons/util/JsonUtil$LevelDeserializer.class
new file mode 100644
index 00000000000..013b1312720
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/util/JsonUtil$LevelDeserializer.class differ
diff --git a/bin/main/seedu/flashlingo/commons/util/JsonUtil.class b/bin/main/seedu/flashlingo/commons/util/JsonUtil.class
new file mode 100644
index 00000000000..fe824719b76
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/util/JsonUtil.class differ
diff --git a/bin/main/seedu/flashlingo/commons/util/StringUtil.class b/bin/main/seedu/flashlingo/commons/util/StringUtil.class
new file mode 100644
index 00000000000..f647752bc5c
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/util/StringUtil.class differ
diff --git a/bin/main/seedu/flashlingo/commons/util/ToStringBuilder.class b/bin/main/seedu/flashlingo/commons/util/ToStringBuilder.class
new file mode 100644
index 00000000000..43aa947e6ba
Binary files /dev/null and b/bin/main/seedu/flashlingo/commons/util/ToStringBuilder.class differ
diff --git a/bin/main/seedu/flashlingo/logic/Logic.class b/bin/main/seedu/flashlingo/logic/Logic.class
new file mode 100644
index 00000000000..443b5c33866
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/Logic.class differ
diff --git a/bin/main/seedu/flashlingo/logic/LogicManager.class b/bin/main/seedu/flashlingo/logic/LogicManager.class
new file mode 100644
index 00000000000..c0b1612f582
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/LogicManager.class differ
diff --git a/bin/main/seedu/flashlingo/logic/Messages.class b/bin/main/seedu/flashlingo/logic/Messages.class
new file mode 100644
index 00000000000..19119b6706c
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/Messages.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/AddCommand.class b/bin/main/seedu/flashlingo/logic/commands/AddCommand.class
new file mode 100644
index 00000000000..f5c1522bbf0
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/AddCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/Command.class b/bin/main/seedu/flashlingo/logic/commands/Command.class
new file mode 100644
index 00000000000..71b4f644e47
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/Command.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/CommandResult.class b/bin/main/seedu/flashlingo/logic/commands/CommandResult.class
new file mode 100644
index 00000000000..73c37b21785
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/CommandResult.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/EditCommand.class b/bin/main/seedu/flashlingo/logic/commands/EditCommand.class
new file mode 100644
index 00000000000..bf4ae9d287d
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/EditCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/ExitCommand.class b/bin/main/seedu/flashlingo/logic/commands/ExitCommand.class
new file mode 100644
index 00000000000..75f4b430d77
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/ExitCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/FindCommand.class b/bin/main/seedu/flashlingo/logic/commands/FindCommand.class
new file mode 100644
index 00000000000..438e535e1c9
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/FindCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/HelpCommand.class b/bin/main/seedu/flashlingo/logic/commands/HelpCommand.class
new file mode 100644
index 00000000000..939b7b81da2
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/HelpCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/LanguageCommand.class b/bin/main/seedu/flashlingo/logic/commands/LanguageCommand.class
new file mode 100644
index 00000000000..3fbe4ec5174
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/LanguageCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/ListCommand.class b/bin/main/seedu/flashlingo/logic/commands/ListCommand.class
new file mode 100644
index 00000000000..5d9c2ad78bf
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/ListCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/LoadCommand.class b/bin/main/seedu/flashlingo/logic/commands/LoadCommand.class
new file mode 100644
index 00000000000..1084946ed0b
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/LoadCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/NoCommand.class b/bin/main/seedu/flashlingo/logic/commands/NoCommand.class
new file mode 100644
index 00000000000..271908813db
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/NoCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/RevealCommand.class b/bin/main/seedu/flashlingo/logic/commands/RevealCommand.class
new file mode 100644
index 00000000000..838ecc98df0
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/RevealCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/ReviewCommand.class b/bin/main/seedu/flashlingo/logic/commands/ReviewCommand.class
new file mode 100644
index 00000000000..739998d205d
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/ReviewCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/StartCommand.class b/bin/main/seedu/flashlingo/logic/commands/StartCommand.class
new file mode 100644
index 00000000000..b9d4112a10d
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/StartCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/StatsCommand.class b/bin/main/seedu/flashlingo/logic/commands/StatsCommand.class
new file mode 100644
index 00000000000..083d4015545
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/StatsCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/SwitchCommand.class b/bin/main/seedu/flashlingo/logic/commands/SwitchCommand.class
new file mode 100644
index 00000000000..8a9b2f96f5a
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/SwitchCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/YesCommand.class b/bin/main/seedu/flashlingo/logic/commands/YesCommand.class
new file mode 100644
index 00000000000..4a664d439f7
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/YesCommand.class differ
diff --git a/bin/main/seedu/flashlingo/logic/commands/exceptions/CommandException.class b/bin/main/seedu/flashlingo/logic/commands/exceptions/CommandException.class
new file mode 100644
index 00000000000..637ed79500e
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/commands/exceptions/CommandException.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/AddCommandParser.class b/bin/main/seedu/flashlingo/logic/parser/AddCommandParser.class
new file mode 100644
index 00000000000..7b73e81afe7
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/AddCommandParser.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/ArgumentMultimap.class b/bin/main/seedu/flashlingo/logic/parser/ArgumentMultimap.class
new file mode 100644
index 00000000000..340ea85a9f5
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/ArgumentMultimap.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/ArgumentTokenizer$PrefixPosition.class b/bin/main/seedu/flashlingo/logic/parser/ArgumentTokenizer$PrefixPosition.class
new file mode 100644
index 00000000000..a8cadccc732
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/ArgumentTokenizer$PrefixPosition.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/ArgumentTokenizer.class b/bin/main/seedu/flashlingo/logic/parser/ArgumentTokenizer.class
new file mode 100644
index 00000000000..5469700e70a
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/ArgumentTokenizer.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/CliSyntax.class b/bin/main/seedu/flashlingo/logic/parser/CliSyntax.class
new file mode 100644
index 00000000000..3a3657e4fbe
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/CliSyntax.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/DeleteCommandParser.class b/bin/main/seedu/flashlingo/logic/parser/DeleteCommandParser.class
new file mode 100644
index 00000000000..b09d69ea8d5
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/DeleteCommandParser.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/FindCommandParser.class b/bin/main/seedu/flashlingo/logic/parser/FindCommandParser.class
new file mode 100644
index 00000000000..3d59cfd7679
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/FindCommandParser.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/FlashlingoParser.class b/bin/main/seedu/flashlingo/logic/parser/FlashlingoParser.class
new file mode 100644
index 00000000000..c2c4a8b42f9
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/FlashlingoParser.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/LanguageCommandParser.class b/bin/main/seedu/flashlingo/logic/parser/LanguageCommandParser.class
new file mode 100644
index 00000000000..40afe02d2bf
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/LanguageCommandParser.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/LoadCommandParser.class b/bin/main/seedu/flashlingo/logic/parser/LoadCommandParser.class
new file mode 100644
index 00000000000..0eda9670cf8
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/LoadCommandParser.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/Parser.class b/bin/main/seedu/flashlingo/logic/parser/Parser.class
new file mode 100644
index 00000000000..8d9fb9c0134
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/Parser.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/ParserUtil.class b/bin/main/seedu/flashlingo/logic/parser/ParserUtil.class
new file mode 100644
index 00000000000..42d1223924a
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/ParserUtil.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/Prefix.class b/bin/main/seedu/flashlingo/logic/parser/Prefix.class
new file mode 100644
index 00000000000..82590143996
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/Prefix.class differ
diff --git a/bin/main/seedu/flashlingo/logic/parser/exceptions/ParseException.class b/bin/main/seedu/flashlingo/logic/parser/exceptions/ParseException.class
new file mode 100644
index 00000000000..c459f3e49ee
Binary files /dev/null and b/bin/main/seedu/flashlingo/logic/parser/exceptions/ParseException.class differ
diff --git a/bin/main/seedu/flashlingo/model/Flashlingo.class b/bin/main/seedu/flashlingo/model/Flashlingo.class
new file mode 100644
index 00000000000..c7d6b7a1ab4
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/Flashlingo.class differ
diff --git a/bin/main/seedu/flashlingo/model/Model.class b/bin/main/seedu/flashlingo/model/Model.class
new file mode 100644
index 00000000000..6367f66c68b
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/Model.class differ
diff --git a/bin/main/seedu/flashlingo/model/ModelManager.class b/bin/main/seedu/flashlingo/model/ModelManager.class
new file mode 100644
index 00000000000..fd78a48e74a
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/ModelManager.class differ
diff --git a/bin/main/seedu/flashlingo/model/ReadOnlyFlashlingo.class b/bin/main/seedu/flashlingo/model/ReadOnlyFlashlingo.class
new file mode 100644
index 00000000000..c04dc061ae1
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/ReadOnlyFlashlingo.class differ
diff --git a/bin/main/seedu/flashlingo/model/ReadOnlyUserPrefs.class b/bin/main/seedu/flashlingo/model/ReadOnlyUserPrefs.class
new file mode 100644
index 00000000000..4f0f71c9bc8
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/ReadOnlyUserPrefs.class differ
diff --git a/bin/main/seedu/flashlingo/model/UserPrefs.class b/bin/main/seedu/flashlingo/model/UserPrefs.class
new file mode 100644
index 00000000000..190be43f416
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/UserPrefs.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/FlashCard.class b/bin/main/seedu/flashlingo/model/flashcard/FlashCard.class
new file mode 100644
index 00000000000..2373facb4ee
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/FlashCard.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/NextReviewWordPredicate.class b/bin/main/seedu/flashlingo/model/flashcard/NextReviewWordPredicate.class
new file mode 100644
index 00000000000..b74de9f52d6
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/NextReviewWordPredicate.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/ProficiencyLevel.class b/bin/main/seedu/flashlingo/model/flashcard/ProficiencyLevel.class
new file mode 100644
index 00000000000..2854a864032
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/ProficiencyLevel.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/UniqueFlashCardList.class b/bin/main/seedu/flashlingo/model/flashcard/UniqueFlashCardList.class
new file mode 100644
index 00000000000..a62da766252
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/UniqueFlashCardList.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/WordContainsKeywordsPredicate.class b/bin/main/seedu/flashlingo/model/flashcard/WordContainsKeywordsPredicate.class
new file mode 100644
index 00000000000..222f15b938f
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/WordContainsKeywordsPredicate.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/WordLanguagePredicate.class b/bin/main/seedu/flashlingo/model/flashcard/WordLanguagePredicate.class
new file mode 100644
index 00000000000..901aa796b9e
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/WordLanguagePredicate.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/WordOverduePredicate.class b/bin/main/seedu/flashlingo/model/flashcard/WordOverduePredicate.class
new file mode 100644
index 00000000000..b74bff812e4
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/WordOverduePredicate.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/exceptions/DuplicateFlashCardException.class b/bin/main/seedu/flashlingo/model/flashcard/exceptions/DuplicateFlashCardException.class
new file mode 100644
index 00000000000..f5cb2bb21f1
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/exceptions/DuplicateFlashCardException.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/exceptions/FlashCardNotFoundException.class b/bin/main/seedu/flashlingo/model/flashcard/exceptions/FlashCardNotFoundException.class
new file mode 100644
index 00000000000..1519371d17e
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/exceptions/FlashCardNotFoundException.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/words/OriginalWord.class b/bin/main/seedu/flashlingo/model/flashcard/words/OriginalWord.class
new file mode 100644
index 00000000000..e13db404e69
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/words/OriginalWord.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/words/TranslatedWord.class b/bin/main/seedu/flashlingo/model/flashcard/words/TranslatedWord.class
new file mode 100644
index 00000000000..df6690a1e2c
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/words/TranslatedWord.class differ
diff --git a/bin/main/seedu/flashlingo/model/flashcard/words/Word.class b/bin/main/seedu/flashlingo/model/flashcard/words/Word.class
new file mode 100644
index 00000000000..d4f2d3d0b76
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/flashcard/words/Word.class differ
diff --git a/bin/main/seedu/flashlingo/model/util/SampleDataUtil.class b/bin/main/seedu/flashlingo/model/util/SampleDataUtil.class
new file mode 100644
index 00000000000..3d92eb98547
Binary files /dev/null and b/bin/main/seedu/flashlingo/model/util/SampleDataUtil.class differ
diff --git a/bin/main/seedu/flashlingo/session/SessionManager.class b/bin/main/seedu/flashlingo/session/SessionManager.class
new file mode 100644
index 00000000000..dc450d6acfe
Binary files /dev/null and b/bin/main/seedu/flashlingo/session/SessionManager.class differ
diff --git a/bin/main/seedu/flashlingo/storage/FlashlingoStorage.class b/bin/main/seedu/flashlingo/storage/FlashlingoStorage.class
new file mode 100644
index 00000000000..466ab456da4
Binary files /dev/null and b/bin/main/seedu/flashlingo/storage/FlashlingoStorage.class differ
diff --git a/bin/main/seedu/flashlingo/storage/JsonAdaptedFlashCard.class b/bin/main/seedu/flashlingo/storage/JsonAdaptedFlashCard.class
new file mode 100644
index 00000000000..aaf4b36510a
Binary files /dev/null and b/bin/main/seedu/flashlingo/storage/JsonAdaptedFlashCard.class differ
diff --git a/bin/main/seedu/flashlingo/storage/JsonFlashlingoStorage.class b/bin/main/seedu/flashlingo/storage/JsonFlashlingoStorage.class
new file mode 100644
index 00000000000..7b1076e70ad
Binary files /dev/null and b/bin/main/seedu/flashlingo/storage/JsonFlashlingoStorage.class differ
diff --git a/bin/main/seedu/flashlingo/storage/JsonSerializableFlashlingo.class b/bin/main/seedu/flashlingo/storage/JsonSerializableFlashlingo.class
new file mode 100644
index 00000000000..5d899115a7b
Binary files /dev/null and b/bin/main/seedu/flashlingo/storage/JsonSerializableFlashlingo.class differ
diff --git a/bin/main/seedu/flashlingo/storage/JsonUserPrefsStorage.class b/bin/main/seedu/flashlingo/storage/JsonUserPrefsStorage.class
new file mode 100644
index 00000000000..3be581add16
Binary files /dev/null and b/bin/main/seedu/flashlingo/storage/JsonUserPrefsStorage.class differ
diff --git a/bin/main/seedu/flashlingo/storage/Storage.class b/bin/main/seedu/flashlingo/storage/Storage.class
new file mode 100644
index 00000000000..7cda8fd1abc
Binary files /dev/null and b/bin/main/seedu/flashlingo/storage/Storage.class differ
diff --git a/bin/main/seedu/flashlingo/storage/StorageManager.class b/bin/main/seedu/flashlingo/storage/StorageManager.class
new file mode 100644
index 00000000000..d259172b36c
Binary files /dev/null and b/bin/main/seedu/flashlingo/storage/StorageManager.class differ
diff --git a/bin/main/seedu/flashlingo/storage/UserPrefsStorage.class b/bin/main/seedu/flashlingo/storage/UserPrefsStorage.class
new file mode 100644
index 00000000000..c44580e59a6
Binary files /dev/null and b/bin/main/seedu/flashlingo/storage/UserPrefsStorage.class differ
diff --git a/bin/main/seedu/flashlingo/ui/CommandBox$CommandExecutor.class b/bin/main/seedu/flashlingo/ui/CommandBox$CommandExecutor.class
new file mode 100644
index 00000000000..0a354c42f3d
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/CommandBox$CommandExecutor.class differ
diff --git a/bin/main/seedu/flashlingo/ui/CommandBox.class b/bin/main/seedu/flashlingo/ui/CommandBox.class
new file mode 100644
index 00000000000..efa9ece0b24
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/CommandBox.class differ
diff --git a/bin/main/seedu/flashlingo/ui/FlashcardBox.class b/bin/main/seedu/flashlingo/ui/FlashcardBox.class
new file mode 100644
index 00000000000..f51a1eb66cf
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/FlashcardBox.class differ
diff --git a/bin/main/seedu/flashlingo/ui/FlashcardBoxNoButton.class b/bin/main/seedu/flashlingo/ui/FlashcardBoxNoButton.class
new file mode 100644
index 00000000000..d2eca4ab22a
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/FlashcardBoxNoButton.class differ
diff --git a/bin/main/seedu/flashlingo/ui/FlashcardListPanel$FlashCardListViewCell.class b/bin/main/seedu/flashlingo/ui/FlashcardListPanel$FlashCardListViewCell.class
new file mode 100644
index 00000000000..5d1aa9ef8a8
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/FlashcardListPanel$FlashCardListViewCell.class differ
diff --git a/bin/main/seedu/flashlingo/ui/FlashcardListPanel.class b/bin/main/seedu/flashlingo/ui/FlashcardListPanel.class
new file mode 100644
index 00000000000..8798d5e6eb9
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/FlashcardListPanel.class differ
diff --git a/bin/main/seedu/flashlingo/ui/HelpWindow.class b/bin/main/seedu/flashlingo/ui/HelpWindow.class
new file mode 100644
index 00000000000..73b216cd052
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/HelpWindow.class differ
diff --git a/bin/main/seedu/flashlingo/ui/MainWindow.class b/bin/main/seedu/flashlingo/ui/MainWindow.class
new file mode 100644
index 00000000000..96d9d4c120c
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/MainWindow.class differ
diff --git a/bin/main/seedu/flashlingo/ui/ResultDisplay.class b/bin/main/seedu/flashlingo/ui/ResultDisplay.class
new file mode 100644
index 00000000000..bf730a47900
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/ResultDisplay.class differ
diff --git a/bin/main/seedu/flashlingo/ui/StatusBarFooter.class b/bin/main/seedu/flashlingo/ui/StatusBarFooter.class
new file mode 100644
index 00000000000..1e15aa485fa
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/StatusBarFooter.class differ
diff --git a/bin/main/seedu/flashlingo/ui/Ui.class b/bin/main/seedu/flashlingo/ui/Ui.class
new file mode 100644
index 00000000000..1855d31c5ff
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/Ui.class differ
diff --git a/bin/main/seedu/flashlingo/ui/UiManager.class b/bin/main/seedu/flashlingo/ui/UiManager.class
new file mode 100644
index 00000000000..1adb74d01a1
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/UiManager.class differ
diff --git a/bin/main/seedu/flashlingo/ui/UiPart.class b/bin/main/seedu/flashlingo/ui/UiPart.class
new file mode 100644
index 00000000000..34146ca7af7
Binary files /dev/null and b/bin/main/seedu/flashlingo/ui/UiPart.class differ
diff --git a/bin/main/view/CommandBox.fxml b/bin/main/view/CommandBox.fxml
new file mode 100644
index 00000000000..779bc3bb556
--- /dev/null
+++ b/bin/main/view/CommandBox.fxml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/bin/main/view/DarkTheme.css b/bin/main/view/DarkTheme.css
new file mode 100644
index 00000000000..366b1de647d
--- /dev/null
+++ b/bin/main/view/DarkTheme.css
@@ -0,0 +1,361 @@
+.background {
+ -fx-background-color: derive(#1d1d1d, 20%);
+ background-color: #383838; /* Used in the default.html file */
+}
+
+.list-cell:empty {
+ /* Empty cells will not have alternating colours */
+ -fx-background: #383838;
+}
+
+.root {
+ -fx-background-color: #383838;
+}
+
+.label {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Courier New";
+ -fx-text-fill: #555555;
+ -fx-opacity: 0.9;
+}
+
+.label-bright {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Courier New";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.label-header {
+ -fx-font-size: 32pt;
+ -fx-font-family: "Courier New";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.text-field {
+ -fx-font-size: 12pt;
+ -fx-font-family: "Courier New";
+}
+
+.tab-pane {
+ -fx-padding: 0 0 0 1;
+}
+
+.tab-pane .tab-header-area {
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
+}
+
+.table-view {
+ -fx-base: #1d1d1d;
+ -fx-control-inner-background: #1d1d1d;
+ -fx-background-color: #1d1d1d;
+ -fx-table-cell-border-color: transparent;
+ -fx-table-header-border-color: transparent;
+ -fx-padding: 5;
+}
+
+.table-view .column-header-background {
+ -fx-background-color: transparent;
+}
+
+.table-view .column-header, .table-view .filler {
+ -fx-size: 35;
+ -fx-border-width: 0 0 1 0;
+ -fx-background-color: transparent;
+ -fx-border-color:
+ transparent
+ transparent
+ derive(-fx-base, 80%)
+ transparent;
+ -fx-border-insets: 0 10 1 0;
+}
+
+.table-view .column-header .label {
+ -fx-font-size: 20pt;
+ -fx-font-family: "Courier New";
+ -fx-text-fill: white;
+ -fx-alignment: center-left;
+ -fx-opacity: 1;
+}
+
+.table-view:focused .table-row-cell:filled:focused:selected {
+ -fx-background-color: -fx-focus-color;
+}
+
+.split-pane:horizontal .split-pane-divider {
+ -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-color: transparent transparent transparent #4d4d4d;
+}
+
+.split-pane {
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.list-view {
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.list-cell {
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap : 0;
+ -fx-padding: 0 0 0 0;
+}
+
+.list-cell:filled:even {
+ -fx-background-color: #3c3e3f;
+}
+
+.list-cell:filled:odd {
+ -fx-background-color: #515658;
+}
+
+.list-cell:filled:selected {
+ -fx-background-color: #424d5f;
+}
+
+.list-cell:filled:selected #cardPane {
+ -fx-border-color: #3e7b91;
+ -fx-border-width: 1;
+}
+
+.list-cell .label {
+ -fx-text-fill: white;
+}
+
+.cell_big_label {
+ -fx-font-family: "Courier New";
+ -fx-font-size: 16px;
+ -fx-text-fill: #010504;
+}
+
+.cell_small_label {
+ -fx-font-family: "Courier New";
+ -fx-font-size: 13px;
+ -fx-text-fill: #010504;
+}
+
+.stack-pane {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.pane-with-border {
+ -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-color: derive(#1d1d1d, 10%);
+ -fx-border-top-width: 1px;
+}
+
+.status-bar {
+ -fx-background-color: derive(#1d1d1d, 30%);
+}
+
+.result-display {
+ -fx-background-color: transparent;
+ -fx-font-family: "Courier New";
+ -fx-font-size: 13pt;
+ -fx-text-fill: white;
+}
+
+.result-display .label {
+ -fx-text-fill: black !important;
+}
+
+.status-bar .label {
+ -fx-font-family: "Courier New";
+ -fx-text-fill: white;
+ -fx-padding: 4px;
+ -fx-pref-height: 30px;
+}
+
+.status-bar-with-border {
+ -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-border-color: derive(#1d1d1d, 25%);
+ -fx-border-width: 1px;
+}
+
+.status-bar-with-border .label {
+ -fx-text-fill: white;
+}
+
+.grid-pane {
+ -fx-background-color: derive(#1d1d1d, 30%);
+ -fx-border-color: derive(#1d1d1d, 30%);
+ -fx-border-width: 1px;
+}
+
+.grid-pane .stack-pane {
+ -fx-background-color: derive(#1d1d1d, 30%);
+}
+
+.context-menu {
+ -fx-background-color: derive(#1d1d1d, 50%);
+}
+
+.context-menu .label {
+ -fx-text-fill: white;
+}
+
+.menu-bar {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.menu-bar .label {
+ -fx-font-size: 14pt;
+ -fx-font-family: "Courier New";
+ -fx-text-fill: white;
+ -fx-opacity: 0.9;
+}
+
+.menu .left-container {
+ -fx-background-color: black;
+}
+
+/*
+ * Metro style Push Button
+ * Author: Pedro Duque Vieira
+ * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
+ */
+.button {
+ -fx-padding: 5 22 5 22;
+ -fx-border-color: #e2e2e2;
+ -fx-border-width: 2;
+ -fx-background-radius: 0;
+ -fx-background-color: #1d1d1d;
+ -fx-font-family: "Courier New", Helvetica, Arial, sans-serif;
+ -fx-font-size: 11pt;
+ -fx-text-fill: #d8d8d8;
+ -fx-background-insets: 0 0 0 0, 0, 1, 2;
+}
+
+.button:hover {
+ -fx-background-color: #3a3a3a;
+}
+
+.button:pressed, .button:default:hover:pressed {
+ -fx-background-color: white;
+ -fx-text-fill: #1d1d1d;
+}
+
+.button:focused {
+ -fx-border-color: white, white;
+ -fx-border-width: 1, 1;
+ -fx-border-style: solid, segments(1, 1);
+ -fx-border-radius: 0, 0;
+ -fx-border-insets: 1 1 1 1, 0;
+}
+
+.button:disabled, .button:default:disabled {
+ -fx-opacity: 0.4;
+ -fx-background-color: #1d1d1d;
+ -fx-text-fill: white;
+}
+
+.button:default {
+ -fx-background-color: -fx-focus-color;
+ -fx-text-fill: #ffffff;
+}
+
+.button:default:hover {
+ -fx-background-color: derive(-fx-focus-color, 30%);
+}
+
+.dialog-pane {
+ -fx-background-color: #1d1d1d;
+}
+
+.dialog-pane > *.button-bar > *.container {
+ -fx-background-color: #1d1d1d;
+}
+
+.dialog-pane > *.label.content {
+ -fx-font-size: 14px;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.dialog-pane:header *.header-panel {
+ -fx-background-color: derive(#1d1d1d, 25%);
+}
+
+.dialog-pane:header *.header-panel *.label {
+ -fx-font-size: 18px;
+ -fx-font-style: italic;
+ -fx-fill: white;
+ -fx-text-fill: white;
+}
+
+.scroll-bar {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.scroll-bar .thumb {
+ -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-insets: 3;
+}
+
+.scroll-bar .increment-button, .scroll-bar .decrement-button {
+ -fx-background-color: transparent;
+ -fx-padding: 0 0 0 0;
+}
+
+.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow {
+ -fx-shape: " ";
+}
+
+.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow {
+ -fx-padding: 1 8 1 8;
+}
+
+.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow {
+ -fx-padding: 8 1 8 1;
+}
+
+#cardPane {
+ -fx-background-color: transparent;
+ -fx-border-width: 0;
+}
+
+#commandTypeLabel {
+ -fx-font-size: 11px;
+ -fx-text-fill: #F70D1A;
+}
+
+#commandTextField {
+ -fx-background-color: transparent #383838 transparent #383838;
+ -fx-background-insets: 0;
+ -fx-border-color: #383838 #383838 #ffffff #383838;
+ -fx-border-insets: 0;
+ -fx-border-width: 1;
+ -fx-font-family: "Courier New";
+ -fx-font-size: 13pt;
+ -fx-text-fill: white;
+}
+
+#filterField, #personListPanel, #personWebpage {
+ -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
+}
+
+#resultDisplay .content {
+ -fx-background-color: transparent, #383838, transparent, #383838;
+ -fx-background-radius: 0;
+}
+
+#tags {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#tags .label {
+ -fx-text-fill: white;
+ -fx-background-color: #3e7b91;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
diff --git a/bin/main/view/Extensions.css b/bin/main/view/Extensions.css
new file mode 100644
index 00000000000..8a7d277d0fb
--- /dev/null
+++ b/bin/main/view/Extensions.css
@@ -0,0 +1,15 @@
+.error {
+ -fx-text-fill: #d06651 !important; /* The error class should always override the default text-fill style */
+}
+
+.tag-selector {
+ -fx-border-width: 1;
+ -fx-border-color: white;
+ -fx-border-radius: 3;
+ -fx-background-radius: 3;
+}
+
+.tooltip-text {
+ -fx-text-fill: white;
+}
+
diff --git a/bin/main/view/FlashcardBox.fxml b/bin/main/view/FlashcardBox.fxml
new file mode 100644
index 00000000000..605c8fecf1b
--- /dev/null
+++ b/bin/main/view/FlashcardBox.fxml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/main/view/FlashcardBoxNoButton.fxml b/bin/main/view/FlashcardBoxNoButton.fxml
new file mode 100644
index 00000000000..977e3f4b0de
--- /dev/null
+++ b/bin/main/view/FlashcardBoxNoButton.fxml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/main/view/FlashcardListCard.fxml b/bin/main/view/FlashcardListCard.fxml
new file mode 100644
index 00000000000..e6f4bf06d43
--- /dev/null
+++ b/bin/main/view/FlashcardListCard.fxml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/PersonListPanel.fxml b/bin/main/view/FlashcardListPanel.fxml
similarity index 77%
rename from src/main/resources/view/PersonListPanel.fxml
rename to bin/main/view/FlashcardListPanel.fxml
index a1bb6bbace8..80077c09b14 100644
--- a/src/main/resources/view/PersonListPanel.fxml
+++ b/bin/main/view/FlashcardListPanel.fxml
@@ -4,5 +4,5 @@
-
+
diff --git a/bin/main/view/HelpWindow.css b/bin/main/view/HelpWindow.css
new file mode 100644
index 00000000000..17e8a8722cd
--- /dev/null
+++ b/bin/main/view/HelpWindow.css
@@ -0,0 +1,19 @@
+#copyButton, #helpMessage {
+ -fx-text-fill: white;
+}
+
+#copyButton {
+ -fx-background-color: dimgray;
+}
+
+#copyButton:hover {
+ -fx-background-color: gray;
+}
+
+#copyButton:armed {
+ -fx-background-color: darkgray;
+}
+
+#helpMessageContainer {
+ -fx-background-color: derive(#1d1d1d, 20%);
+}
diff --git a/bin/main/view/HelpWindow.fxml b/bin/main/view/HelpWindow.fxml
new file mode 100644
index 00000000000..e01f330de33
--- /dev/null
+++ b/bin/main/view/HelpWindow.fxml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/main/view/LightTheme.css b/bin/main/view/LightTheme.css
new file mode 100644
index 00000000000..9c48479be2d
--- /dev/null
+++ b/bin/main/view/LightTheme.css
@@ -0,0 +1,268 @@
+.background {
+ -fx-background-color: #f0f0f0;
+}
+
+.list-cell:empty {
+ /* Empty cells will not have alternating colours */
+ -fx-background: #f0f0f0;
+}
+
+.root {
+ -fx-font-family: "Courier New";
+ -fx-background-color: #f0f0f0;
+}
+
+.label {
+ -fx-font-size: 13pt;
+ -fx-text-fill: #555555;
+ -fx-opacity: 0.9;
+}
+
+.label-bright {
+ -fx-font-size: 13pt;
+ -fx-text-fill: black;
+ -fx-opacity: 1;
+}
+
+.label-header {
+ -fx-font-size: 32pt;
+ -fx-text-fill: black;
+ -fx-opacity: 1;
+}
+
+.text-field {
+ -fx-font-size: 13pt;
+}
+
+.tab-pane {
+ -fx-padding: 0 0 0 1;
+}
+
+.tab-pane .tab-header-area {
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
+}
+
+.table-view {
+ -fx-base: #f0f0f0;
+ -fx-control-inner-background: #f0f0f0;
+ -fx-background-color: #f0f0f0;
+ -fx-table-cell-border-color: transparent;
+ -fx-table-header-border-color: transparent;
+ -fx-padding: 5;
+}
+
+.table-view .column-header-background {
+ -fx-background-color: #f0f0f0;
+}
+
+.table-view .column-header, .table-view .filler {
+ -fx-size: 35;
+ -fx-border-width: 0 0 1 0;
+ -fx-background-color: #f0f0f0;
+ -fx-border-color:
+ transparent
+ transparent
+ derive(-fx-base, 80%)
+ transparent;
+ -fx-border-insets: 0 10 1 0;
+}
+
+.table-view .column-header .label {
+ -fx-font-size: 20pt;
+ -fx-text-fill: black;
+ -fx-alignment: center-left;
+ -fx-opacity: 1;
+}
+
+.table-view:focused .table-row-cell:filled:focused:selected {
+ -fx-background-color: -fx-focus-color;
+}
+
+.split-pane:horizontal .split-pane-divider {
+ -fx-background-color: #f0f0f0;
+ -fx-border-color: transparent transparent transparent #4d4d4d;
+}
+
+.split-pane {
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: #f0f0f0;
+}
+
+.list-view {
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+ -fx-background-color: #f0f0f0;
+}
+
+.list-cell {
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap : 0;
+ -fx-padding: 0 0 0 0;
+}
+
+.list-cell:filled:even {
+ -fx-background-color: #b5c4b1;
+}
+
+.list-cell:filled:odd {
+ -fx-background-color: #96a48b;
+}
+
+.list-cell:filled:selected {
+ -fx-background-color: #7b8b6f;
+}
+
+.list-cell:filled:selected #cardPane {
+ -fx-border-color: #f0f0f0;
+ -fx-border-width: 1;
+}
+
+.list-cell .label {
+ -fx-text-fill: black;
+}
+
+.cell_big_label {
+ -fx-font-size: 13pt;
+ -fx-font-weight: bold;
+ -fx-text-fill: black;
+}
+
+.cell_small_label {
+ -fx-font-size: 11pt;
+ -fx-text-fill: black;
+}
+
+.stack-pane {
+ -fx-background-color: #f0f0f0;
+}
+
+.pane-with-border{
+ -fx-background-color: #f0f0f0;
+}
+
+.status-bar {
+ -fx-background-color: #f0f0f0;
+}
+
+.command-box {
+ -fx-background-color: #f0f0f0;
+ -fx-border-color: #f0f0f0;
+ -fx-font-size: 13pt;
+ -fx-font-weight: bold;
+}
+
+.result-display {
+ -fx-background-color: #f0f0f0;
+ -fx-border-color: #7b8b6f #f0f0f0 #f0f0f0 #f0f0f0;
+ -fx-border-width: 4;
+ -fx-font-weight: bold;
+ -fx-font-size: 13pt;
+ -fx-text-fill: #d3ad97;
+}
+
+.result-display .label {
+ -fx-background-color: #f0f0f0;
+ -fx-text-fill: black !important;
+}
+
+.result-display .content {
+ -fx-background-color: #f0f0f0;
+}
+
+.status-bar .label {
+ -fx-text-fill: black;
+ -fx-padding: 4px;
+ -fx-pref-height: 30px;
+}
+
+.status-bar-with-border {
+ -fx-background-color: #e0e0e0;
+ -fx-border-color: #d0d0d0;
+ -fx-border-width: 1px;
+}
+
+.status-bar-with-border .label {
+ -fx-text-fill: black;
+}
+
+.grid-pane {
+ -fx-background-color: #e0e0e0;
+ -fx-border-color: #d0d0d0;
+ -fx-border-width: 1px;
+}
+
+.grid-pane .stack-pane {
+ -fx-background-color: #f0f0f0;
+}
+
+.context-menu {
+ -fx-background-color: #f0f0f0;
+}
+
+.context-menu .label {
+ -fx-text-fill: black;
+}
+
+.menu-bar {
+ -fx-background-color: #f0f0f0;
+}
+
+.menu-bar .label {
+ -fx-font-size: 13pt;
+ -fx-text-fill: black;
+ -fx-opacity: 0.9;
+}
+
+.menu .left-container {
+ -fx-background-color: #f0f0f0;
+}
+
+.button {
+ -fx-padding: 4 20 4 20;
+ -fx-border-width: 2;
+ -fx-background-radius: 20;
+ -fx-background-color: white;
+ -fx-background-radius: 3px;
+ -fx-font-size: 11pt;
+}
+
+.button:hover {
+ -fx-background-color: #f1d3c2;
+ -fx-font-size: 11pt;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.button:pressed {
+ -fx-background-color: #ebc1a8;
+ -fx-font-size: 11pt;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.menu-item {
+ -fx-background-color: #f0f0f0;
+}
+
+.menu-item:hover {
+ -fx-background-color: #f1d3c2;
+}
+
+.menu-item:pressed {
+ -fx-background-color: #ebc1a8;
+}
+
+.menu {
+ -fx-background-color: #f0f0f0;
+}
+
+.menu:hover {
+ -fx-background-color: #f1d3c2;
+}
+
+.menu:pressed {
+ -fx-background-color: #ebc1a8;
+}
diff --git a/bin/main/view/MainWindow.fxml b/bin/main/view/MainWindow.fxml
new file mode 100644
index 00000000000..67056bd515f
--- /dev/null
+++ b/bin/main/view/MainWindow.fxml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/main/view/ResultDisplay.fxml b/bin/main/view/ResultDisplay.fxml
new file mode 100644
index 00000000000..611a773592b
--- /dev/null
+++ b/bin/main/view/ResultDisplay.fxml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
diff --git a/bin/main/view/StatusBarFooter.fxml b/bin/main/view/StatusBarFooter.fxml
new file mode 100644
index 00000000000..7b430f9c6a2
--- /dev/null
+++ b/bin/main/view/StatusBarFooter.fxml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/test/seedu/flashlingo/commons/core/ConfigTest.class b/bin/test/seedu/flashlingo/commons/core/ConfigTest.class
new file mode 100644
index 00000000000..06ff6813bae
Binary files /dev/null and b/bin/test/seedu/flashlingo/commons/core/ConfigTest.class differ
diff --git a/bin/test/seedu/flashlingo/commons/core/GuiSettingsTest.class b/bin/test/seedu/flashlingo/commons/core/GuiSettingsTest.class
new file mode 100644
index 00000000000..7625c5268fb
Binary files /dev/null and b/bin/test/seedu/flashlingo/commons/core/GuiSettingsTest.class differ
diff --git a/bin/test/seedu/flashlingo/commons/core/VersionTest.class b/bin/test/seedu/flashlingo/commons/core/VersionTest.class
new file mode 100644
index 00000000000..4fee2346308
Binary files /dev/null and b/bin/test/seedu/flashlingo/commons/core/VersionTest.class differ
diff --git a/bin/test/seedu/flashlingo/commons/core/index/IndexTest.class b/bin/test/seedu/flashlingo/commons/core/index/IndexTest.class
new file mode 100644
index 00000000000..3b4e3dcfd51
Binary files /dev/null and b/bin/test/seedu/flashlingo/commons/core/index/IndexTest.class differ
diff --git a/bin/test/seedu/flashlingo/commons/util/AppUtilTest.class b/bin/test/seedu/flashlingo/commons/util/AppUtilTest.class
new file mode 100644
index 00000000000..2c59bd63ccb
Binary files /dev/null and b/bin/test/seedu/flashlingo/commons/util/AppUtilTest.class differ
diff --git a/bin/test/seedu/flashlingo/commons/util/CollectionUtilTest.class b/bin/test/seedu/flashlingo/commons/util/CollectionUtilTest.class
new file mode 100644
index 00000000000..0778065c9fe
Binary files /dev/null and b/bin/test/seedu/flashlingo/commons/util/CollectionUtilTest.class differ
diff --git a/bin/test/seedu/flashlingo/commons/util/ConfigUtilTest.class b/bin/test/seedu/flashlingo/commons/util/ConfigUtilTest.class
new file mode 100644
index 00000000000..87e10e3660a
Binary files /dev/null and b/bin/test/seedu/flashlingo/commons/util/ConfigUtilTest.class differ
diff --git a/bin/test/seedu/flashlingo/commons/util/FileUtilTest.class b/bin/test/seedu/flashlingo/commons/util/FileUtilTest.class
new file mode 100644
index 00000000000..1552d660bcb
Binary files /dev/null and b/bin/test/seedu/flashlingo/commons/util/FileUtilTest.class differ
diff --git a/bin/test/seedu/flashlingo/commons/util/StringUtilTest.class b/bin/test/seedu/flashlingo/commons/util/StringUtilTest.class
new file mode 100644
index 00000000000..85ede3d133f
Binary files /dev/null and b/bin/test/seedu/flashlingo/commons/util/StringUtilTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/LogicManagerTest$1.class b/bin/test/seedu/flashlingo/logic/LogicManagerTest$1.class
new file mode 100644
index 00000000000..06bed503f1f
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/LogicManagerTest$1.class differ
diff --git a/bin/test/seedu/flashlingo/logic/LogicManagerTest.class b/bin/test/seedu/flashlingo/logic/LogicManagerTest.class
new file mode 100644
index 00000000000..7f43f7c3a31
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/LogicManagerTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/AddCommandTest.class b/bin/test/seedu/flashlingo/logic/commands/AddCommandTest.class
new file mode 100644
index 00000000000..25b61918985
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/AddCommandTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/CommandResultTest.class b/bin/test/seedu/flashlingo/logic/commands/CommandResultTest.class
new file mode 100644
index 00000000000..800516cf44a
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/CommandResultTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/CommandTestUtil.class b/bin/test/seedu/flashlingo/logic/commands/CommandTestUtil.class
new file mode 100644
index 00000000000..a83bf0a1ab6
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/CommandTestUtil.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/DeleteCommandTest.class b/bin/test/seedu/flashlingo/logic/commands/DeleteCommandTest.class
new file mode 100644
index 00000000000..dc430bffe90
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/DeleteCommandTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/EditCommandTest.class b/bin/test/seedu/flashlingo/logic/commands/EditCommandTest.class
new file mode 100644
index 00000000000..c343395d42b
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/EditCommandTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/ExitCommandTest.class b/bin/test/seedu/flashlingo/logic/commands/ExitCommandTest.class
new file mode 100644
index 00000000000..435e15e91da
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/ExitCommandTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/FindCommandTest.class b/bin/test/seedu/flashlingo/logic/commands/FindCommandTest.class
new file mode 100644
index 00000000000..61126d1c2b7
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/FindCommandTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/HelpCommandTest.class b/bin/test/seedu/flashlingo/logic/commands/HelpCommandTest.class
new file mode 100644
index 00000000000..25cda0ed7f9
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/HelpCommandTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/ListCommandTest.class b/bin/test/seedu/flashlingo/logic/commands/ListCommandTest.class
new file mode 100644
index 00000000000..92133863bbe
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/ListCommandTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/LoadCommandTest.class b/bin/test/seedu/flashlingo/logic/commands/LoadCommandTest.class
new file mode 100644
index 00000000000..e4e053f2a8b
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/LoadCommandTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/SwitchCommandTest.class b/bin/test/seedu/flashlingo/logic/commands/SwitchCommandTest.class
new file mode 100644
index 00000000000..05409510b40
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/SwitchCommandTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/commands/YesCommandTest.class b/bin/test/seedu/flashlingo/logic/commands/YesCommandTest.class
new file mode 100644
index 00000000000..54e9bd5a5b8
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/commands/YesCommandTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/parser/AddCommandParserTest.class b/bin/test/seedu/flashlingo/logic/parser/AddCommandParserTest.class
new file mode 100644
index 00000000000..b0cc8f0065b
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/parser/AddCommandParserTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/parser/ArgumentTokenizerTest.class b/bin/test/seedu/flashlingo/logic/parser/ArgumentTokenizerTest.class
new file mode 100644
index 00000000000..d92b865bb95
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/parser/ArgumentTokenizerTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/parser/CommandParserTestUtil.class b/bin/test/seedu/flashlingo/logic/parser/CommandParserTestUtil.class
new file mode 100644
index 00000000000..dae778a81a6
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/parser/CommandParserTestUtil.class differ
diff --git a/bin/test/seedu/flashlingo/logic/parser/DeleteCommandParserTest.class b/bin/test/seedu/flashlingo/logic/parser/DeleteCommandParserTest.class
new file mode 100644
index 00000000000..c8739800037
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/parser/DeleteCommandParserTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/parser/EditCommandParserTest.class b/bin/test/seedu/flashlingo/logic/parser/EditCommandParserTest.class
new file mode 100644
index 00000000000..4c7ff5790b2
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/parser/EditCommandParserTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/parser/FindCommandParserTest.class b/bin/test/seedu/flashlingo/logic/parser/FindCommandParserTest.class
new file mode 100644
index 00000000000..d79574ab6f1
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/parser/FindCommandParserTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/parser/FlashlingoParserTest.class b/bin/test/seedu/flashlingo/logic/parser/FlashlingoParserTest.class
new file mode 100644
index 00000000000..bec3332d142
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/parser/FlashlingoParserTest.class differ
diff --git a/bin/test/seedu/flashlingo/logic/parser/LoadCommandParserTest.class b/bin/test/seedu/flashlingo/logic/parser/LoadCommandParserTest.class
new file mode 100644
index 00000000000..7a0403801b1
Binary files /dev/null and b/bin/test/seedu/flashlingo/logic/parser/LoadCommandParserTest.class differ
diff --git a/bin/test/seedu/flashlingo/model/ModelManagerTest.class b/bin/test/seedu/flashlingo/model/ModelManagerTest.class
new file mode 100644
index 00000000000..4756e64f99c
Binary files /dev/null and b/bin/test/seedu/flashlingo/model/ModelManagerTest.class differ
diff --git a/bin/test/seedu/flashlingo/model/flashcard/FlashCardTest.class b/bin/test/seedu/flashlingo/model/flashcard/FlashCardTest.class
new file mode 100644
index 00000000000..846a9d34a96
Binary files /dev/null and b/bin/test/seedu/flashlingo/model/flashcard/FlashCardTest.class differ
diff --git a/bin/test/seedu/flashlingo/model/flashcard/ProficiencyLevelTest.class b/bin/test/seedu/flashlingo/model/flashcard/ProficiencyLevelTest.class
new file mode 100644
index 00000000000..740b9836faa
Binary files /dev/null and b/bin/test/seedu/flashlingo/model/flashcard/ProficiencyLevelTest.class differ
diff --git a/bin/test/seedu/flashlingo/model/flashcard/words/OriginalWordTest.class b/bin/test/seedu/flashlingo/model/flashcard/words/OriginalWordTest.class
new file mode 100644
index 00000000000..401f1361609
Binary files /dev/null and b/bin/test/seedu/flashlingo/model/flashcard/words/OriginalWordTest.class differ
diff --git a/bin/test/seedu/flashlingo/model/flashcard/words/TranslatedWordTest.class b/bin/test/seedu/flashlingo/model/flashcard/words/TranslatedWordTest.class
new file mode 100644
index 00000000000..634b7e3828a
Binary files /dev/null and b/bin/test/seedu/flashlingo/model/flashcard/words/TranslatedWordTest.class differ
diff --git a/bin/test/seedu/flashlingo/storage/JsonAdaptedFlashCardTest.class b/bin/test/seedu/flashlingo/storage/JsonAdaptedFlashCardTest.class
new file mode 100644
index 00000000000..dc0614a6b68
Binary files /dev/null and b/bin/test/seedu/flashlingo/storage/JsonAdaptedFlashCardTest.class differ
diff --git a/bin/test/seedu/flashlingo/storage/JsonFlashlingoStorageTest.class b/bin/test/seedu/flashlingo/storage/JsonFlashlingoStorageTest.class
new file mode 100644
index 00000000000..cc767e1e0d7
Binary files /dev/null and b/bin/test/seedu/flashlingo/storage/JsonFlashlingoStorageTest.class differ
diff --git a/bin/test/seedu/flashlingo/storage/JsonSerializableFlashlingoTest.class b/bin/test/seedu/flashlingo/storage/JsonSerializableFlashlingoTest.class
new file mode 100644
index 00000000000..5050f77d57a
Binary files /dev/null and b/bin/test/seedu/flashlingo/storage/JsonSerializableFlashlingoTest.class differ
diff --git a/bin/test/seedu/flashlingo/storage/JsonUserPrefsStorageTest.class b/bin/test/seedu/flashlingo/storage/JsonUserPrefsStorageTest.class
new file mode 100644
index 00000000000..9edb4dfbe41
Binary files /dev/null and b/bin/test/seedu/flashlingo/storage/JsonUserPrefsStorageTest.class differ
diff --git a/bin/test/seedu/flashlingo/storage/StorageManagerTest.class b/bin/test/seedu/flashlingo/storage/StorageManagerTest.class
new file mode 100644
index 00000000000..19a31cebb72
Binary files /dev/null and b/bin/test/seedu/flashlingo/storage/StorageManagerTest.class differ
diff --git a/bin/test/seedu/flashlingo/testutil/Assert.class b/bin/test/seedu/flashlingo/testutil/Assert.class
new file mode 100644
index 00000000000..5fb5a5a307e
Binary files /dev/null and b/bin/test/seedu/flashlingo/testutil/Assert.class differ
diff --git a/bin/test/seedu/flashlingo/testutil/FlashCardUtil.class b/bin/test/seedu/flashlingo/testutil/FlashCardUtil.class
new file mode 100644
index 00000000000..fea9e1767ec
Binary files /dev/null and b/bin/test/seedu/flashlingo/testutil/FlashCardUtil.class differ
diff --git a/bin/test/seedu/flashlingo/testutil/FlashcardBuilder.class b/bin/test/seedu/flashlingo/testutil/FlashcardBuilder.class
new file mode 100644
index 00000000000..ac275b09dbe
Binary files /dev/null and b/bin/test/seedu/flashlingo/testutil/FlashcardBuilder.class differ
diff --git a/bin/test/seedu/flashlingo/testutil/FlashlingoBuilder.class b/bin/test/seedu/flashlingo/testutil/FlashlingoBuilder.class
new file mode 100644
index 00000000000..e690a07fcd5
Binary files /dev/null and b/bin/test/seedu/flashlingo/testutil/FlashlingoBuilder.class differ
diff --git a/bin/test/seedu/flashlingo/testutil/TestUtil.class b/bin/test/seedu/flashlingo/testutil/TestUtil.class
new file mode 100644
index 00000000000..4d7c35e9f09
Binary files /dev/null and b/bin/test/seedu/flashlingo/testutil/TestUtil.class differ
diff --git a/bin/test/seedu/flashlingo/testutil/TypicalFlashCards.class b/bin/test/seedu/flashlingo/testutil/TypicalFlashCards.class
new file mode 100644
index 00000000000..79970eeb520
Binary files /dev/null and b/bin/test/seedu/flashlingo/testutil/TypicalFlashCards.class differ
diff --git a/bin/test/seedu/flashlingo/testutil/TypicalIndexes.class b/bin/test/seedu/flashlingo/testutil/TypicalIndexes.class
new file mode 100644
index 00000000000..fc91294901d
Binary files /dev/null and b/bin/test/seedu/flashlingo/testutil/TypicalIndexes.class differ
diff --git a/bin/test/view/UiPartTest/invalidFile.fxml b/bin/test/view/UiPartTest/invalidFile.fxml
new file mode 100644
index 00000000000..67680946732
--- /dev/null
+++ b/bin/test/view/UiPartTest/invalidFile.fxml
@@ -0,0 +1 @@
+Not a valid FXML file
diff --git a/bin/test/view/UiPartTest/validFile.fxml b/bin/test/view/UiPartTest/validFile.fxml
new file mode 100644
index 00000000000..b20d955fdf4
--- /dev/null
+++ b/bin/test/view/UiPartTest/validFile.fxml
@@ -0,0 +1,4 @@
+
+
+
+Hello World!
diff --git a/bin/test/view/UiPartTest/validFileWithFxRoot.fxml b/bin/test/view/UiPartTest/validFileWithFxRoot.fxml
new file mode 100644
index 00000000000..1a8b2c9e4d3
--- /dev/null
+++ b/bin/test/view/UiPartTest/validFileWithFxRoot.fxml
@@ -0,0 +1,6 @@
+
+
+
+ Hello World!
+
diff --git a/build.gradle b/build.gradle
index a2951cc709e..af57f977a3d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,7 @@ plugins {
id 'jacoco'
}
-mainClassName = 'seedu.address.Main'
+mainClassName = 'seedu.flashlingo.Main'
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
@@ -18,6 +18,8 @@ repositories {
checkstyle {
toolVersion = '10.2'
+ configFile = file('config/checkstyle/checkstyle.xml')
+ checkstyleMain.exclude '**/docs/**'
}
test {
@@ -56,17 +58,23 @@ dependencies {
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win'
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac'
implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux'
-
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.0'
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
+
+ // https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml
+ implementation group: 'org.apache.poi', name: 'poi-ooxml', version: '3.6'
}
shadowJar {
- archiveFileName = 'addressbook.jar'
+ archiveFileName = 'flashlingo.jar'
+}
+
+run {
+ enableAssertions = true
}
defaultTasks 'clean', 'test'
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 1c9514e966a..e9ef26e6fc1 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -9,51 +9,52 @@ You can reach us at the email `seer[at]comp.nus.edu.sg`
## Project team
-### John Doe
+### Wang Cheng
-
+
-[[homepage](http://www.comp.nus.edu.sg/~damithch)]
-[[github](https://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/wangcheng0116)]
+[[portfolio](team/wangcheng0116.md)]
-* Role: Project Advisor
+* Role: Developer
+* Responsibilities: Commands Implementation and bug fixing
-### Jane Doe
+### Yu Jiali
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/a1waysd)]
+[[portfolio](team/a1waysd.md)]
-* Role: Team Lead
-* Responsibilities: UI
+* Role: Scheduling and tracking, Lead
+* Responsibilities: Backend, UI
-### Johnny Doe
+### TAN SHEN, NATHANAEL MARK
-
+
-[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)]
+[[github](https://github.com/itsNatTan)]
+[[portfolio](team/itsnattan.md)]
-* Role: Developer
-* Responsibilities: Data
+* Role: Lead, QA, Developer
+* Responsibilities: Backend, UI
-### Jean Doe
+### BHARDWAJ TAANISH
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/D-Limiter)]
+[[portfolio](team/d_limiter.md)]
* Role: Developer
-* Responsibilities: Dev Ops + Threading
+* Responsibilities: UI and backend
-### James Doe
+### Song Mengfei
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/Song-Mengfei)]
+[[portfolio](team/song-mengfei.md)]
* Role: Developer
-* Responsibilities: UI
+* Responsibilities: Backend and Bug Fixing
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 8a861859bfd..9d84dd906ac 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -2,14 +2,16 @@
layout: page
title: Developer Guide
---
+
* Table of Contents
{:toc}
---------------------------------------------------------------------------------------------------------------------
+---
## **Acknowledgements**
-* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
+Third-party Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5), [Apache POI](https://poi.apache.org/)
--------------------------------------------------------------------------------------------------------------------
@@ -36,7 +38,7 @@ Given below is a quick overview of main components and how they interact with ea
**Main components of the architecture**
-**`Main`** (consisting of classes [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java)) is in charge of the app launch and shut down.
+**`Main`** (consisting of classes [`Main.java`](https://github.com/AY2324S1-CS2103T-T11-4/tp/blob/master/src/main/java/seedu/flashlingo/Main.java) and [`MainApp.java`](https://github.com/AY2324S1-CS2103T-T11-4/tp/blob/master/src/main/java/seedu/flashlingo/MainApp.java)) is in charge of the app launch and shut down.
* At app launch, it initializes the other components in the correct sequence, and connects them up with each other.
* At shut down, it shuts down the other components and invokes cleanup methods where necessary.
@@ -68,24 +70,24 @@ The sections below give more details of each component.
### UI component
-The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java)
+The **API** of this component is specified in [`Ui.java`](https://github.com/AY2324S1-CS2103T-T11-4/tp/blob/master/src/main/java/seedu/flashlingo/ui/Ui.java)
![Structure of the UI Component](images/UiClassDiagram.png)
-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
+The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `FlashCardListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
-The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml)
+The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2324S1-CS2103T-T11-4/tp/blob/master/src/main/java/seedu/flashlingo/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2324S1-CS2103T-T11-4/tp/blob/master/src/main/resources/view/MainWindow.fxml)
The `UI` component,
* executes user commands using the `Logic` component.
* listens for changes to `Model` data so that the UI can be updated with the modified data.
* keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands.
-* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`.
+* depends on some classes in the `Model` component, as it displays `Flashcard` object residing in the `Model`.
### Logic component
-**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java)
+**API** : [`Logic.java`](https://github.com/AY2324S1-CS2103T-T11-4/tp/blob/master/src/main/java/seedu/flashlingo/logic/Logic.java)
Here's a (partial) class diagram of the `Logic` component:
@@ -100,9 +102,9 @@ The sequence diagram below illustrates the interactions within the `Logic` compo
How the `Logic` component works:
-1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command.
+1. When `Logic` is called upon to execute a command, it is passed to an `FlashlingoParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command.
1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`.
-1. The command can communicate with the `Model` when it is executed (e.g. to delete a person).
+1. The command can communicate with the `Model` when it is executed (e.g. to delete a FlashCard).
1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`.
Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command:
@@ -110,43 +112,37 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha
How the parsing works:
-* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object.
+* When called upon to parse a user command, the `FlashlingoParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `FlashlingoParser` returns back as a `Command` object.
* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing.
### Model component
-**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java)
+**API** : [`Model.java`](docs/images/ModelClassDiagram.png)
The `Model` component,
-* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object).
-* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
+* stores the flashlingo data i.e., all `FlashCard` objects (which are contained in a `UniqueFlashCardList` object).
+* stores the currently 'selected' `FlashCard` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects.
* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components)
-
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
-
-
-
-
-
### Storage component
-**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java)
+**API** : [`Storage.java`](docs/images/StorageClassDiagram.png)
The `Storage` component,
-* can save both address book data and user preference data in JSON format, and read them back into corresponding objects.
-* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
+* can save both flashlingo data and user preference data in JSON format, and read them back into corresponding objects.
+* inherits from both `FlashlingoStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`)
### Common classes
-Classes used by multiple components are in the `seedu.addressbook.commons` package.
+Classes used by multiple components are in the `seedu.flashlingo.commons` package.
--------------------------------------------------------------------------------------------------------------------
@@ -154,89 +150,317 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa
This section describes some noteworthy details on how certain features are implemented.
-### \[Proposed\] Undo/redo feature
-#### Proposed Implementation
+### UI enhancement (Nathanael M. Tan)
-The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations:
+#### Implementation
-* `VersionedAddressBook#commit()` — Saves the current address book state in its history.
-* `VersionedAddressBook#undo()` — Restores the previous address book state from its history.
-* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history.
+Display of flashcard details is split into two different classes of `FlashcardBox` and `FlashcardBoxNoButtons`
+FlashcardBoxNoButtons is the default way to display the details of the flashcard.\
+When `start` command is used, review session begins and FlashcardBox is then used to display the details.\
+This is to prevent users from reviewing cards that they are not scheduled to review and erroneously cause changes to the flashcard.
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+Both `FlashcardBox` and `FlashcardBoxNoButtons` have a button at the right called that is called "Reveal" when the translation is not shown, and "Hide" when it is.\
+This button will toggle the state of whether the translation is currently displayed.
-Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
+`FlashcardBox` has an additional two buttons, the `Yes` button and the `No` button.\
+The `Yes` button will invoke the yes command, just like if it were to be typed into the command line
+Similarly, the `No` button will invoke the no command.
-Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state.
+#### Design considerations:
-![UndoRedoState0](images/UndoRedoState0.png)
+**Aspect: How to invoke the command:**
-Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state.
+* **Alternative 1 (current choice):** Pass MainWindow all the way into FlashcardBox. Use `executeCommand()` method to invoke the respective command
+ * Pros:
+ * High level of maintainability.
+ * Outcome will be the same as if it were to be typed into the CLI.
+ * Easy to change logic of the commands
+ * Cons:
+ * Have to pass MainWindow through multiple classes
+ * Classes that do not need references to MainWindow are now forced to have them
-![UndoRedoState1](images/UndoRedoState1.png)
+* **Alternative 2:** Individual button can perform the `Yes` and `No` command by itself, without executing through a Command
+ * Pros:
+ * Don't have to keep reference to the MainWindow
+ * Cons:
+ * Low level of maintainability.
+ * Changes made have to be replicated in different places.
+ * May not behave the same way as a Command (eg. ResultDisplay does not show the log message)
-Step 3. The user executes `add n/David …` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`.
+* **Alternative 3:** Remove the `Yes` and `No` buttons.
+ * Pros:
+ * Easy to code
+ * Only one way to invoke the command, reduce confusion
+ * Cons:
+ * Less convenient without the buttons, needing to type
-![UndoRedoState2](images/UndoRedoState2.png)
+### Sequence diagram when clicking the `Yes` and `No` buttons
+#### Both diagrams are the same except for the inputs to the methods.
+#### Refer to the [Yes Command diagram](#YesSequenceDiagram) for a more detailed insight into the Model
+![YesCommand from FlashcardBox](images/UiYesButtonSequenceDiagram.png)
-
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`.
+![NoCommand from FlashcardBox](images/UiNoButtonSequenceDiagram.png)
-
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state.
+### Start and End Session
-![UndoRedoState3](images/UndoRedoState3.png)
+#### **Feature Overview**
-
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather
-than attempting to perform the undo.
+The "Start and End Review Session" feature is designed for users to start or end a review session. It allows users to initiate and conclude dedicated language learning sessions where they focus on reviewing and practicing vocabulary words. Each session represents a focused period of language reviewing within the application.
-
+#### **Implementation**
-The following sequence diagram shows how the undo operation works:
+The implementation of the "Start and End Review Session" feature involves the introduction of a `SessionManager` component. The `SessionManager` tracks the following aspect of a review session:
-![UndoSequenceDiagram](images/UndoSequenceDiagram.png)
+- **isReviewSession:** Indicates whether the application in a review session or not.
-
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+In addition to that, `start` and `end` commands and their corresponding parsers are also implemented.
-
+Given below is an example usage scenario and how the `start/end` mechanism behaves at each step.
-The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state.
+**Step 1:** The user launches the application for the first time. The `SessionManager` is not yet initialized.
-
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+**Step 2:** The user executes the `start` command by interacting with the command line. This will make `FlashlingoParser` class to create its `SessionManager` instance.
+
+
-Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged.
+
+**Note**: The `SessionManager` class adheres to the **Singleton pattern**, guaranteeing that only one instance of the class
+can exist. This architectural choice provides a single point of access for managing review sessions and
+maintaining the state of whether the session is a review session or not. With the Singleton pattern in place, you can be
+confident that there is only one `SessionManager` instance, making it a centralized and controlled entity for session
+management within the application.
-![UndoRedoState4](images/UndoRedoState4.png)
+**Step 3:** The user executes various commands within the action sequence, such as `yes` and `no`.
-Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …` command. This is the behavior that most modern desktop applications follow.
+**Step 4:** The user chooses to end the review session by using `end` command. This action will alternate the boolean value
+inside SessionManager class indicating current session is review session or not.
-![UndoRedoState5](images/UndoRedoState5.png)
+Below is the sequence diagram for the above scenario:
-The following activity diagram summarizes what happens when a user executes a new command:
+![StartSequenceModel](images/StartSequenceDiagram.png)
+
-
+**User Control:**
+- Users can initiate a new language learning session at any time and conclude their session when they have completed their vocabulary review.
-#### Design considerations:
+**Privacy and Security:**
+- The application should ensure the privacy and security of session data, particularly if it contains sensitive language learning content.
+
+#### **Usage Example**
+
+1. **Starting a Language Learning Session:** The user accesses the language learning module within the application and chooses to start a new language learning session.
+
+2. **Vocabulary Review:** During the session, the user focuses on reviewing and practicing specific vocabulary words and phrases relevant to their language learning goals.
+
+3. **Ending the Language Learning Session:** When the user is satisfied with their vocabulary review, they conclude the language learning session from the language learning module or settings.
+
+#### **Design Considerations**
+
+**Aspect: How `start/end` executes**
+* Alternative 1 (current choice): Creating another separate class to manage the logic.
+ * Pros: It better adheres to OOP principle and easier to maintain.
+ * Cons: It may potentially increase the complexity of codes.
+
+* Alternative 2: Introducing a boolean attribute inside `FlashlingoParser` class.
+ * Pros: Easy to implement.
+ * Cons: It doesn't conform to the principle of **Single Responsibility Principle**.
+
+
+**Aspect: Preventing Using A Set of Commands Within a Review Session**
+
+* Alternative 1 (Current Choice): Restricting Users with a Subset of Commands
+
+ * Pros:
+ - Increased safety: A limited set of commands reduces the risk of unintended actions, making the review session safer for users.
+
+ * Cons:
+ - Limited flexibility: Users may feel constrained if they need to perform specific actions that are not allowed within the review session.
+ - Potential user frustration: Restricting commands may lead to user frustration if they can't perform certain actions they expected to be available.
+
+* Alternative 2: Giving Users Full Flexibility to Execute All Commands
+
+ * Pros:
+ - Complete control: Users have the freedom to use any command, providing them with full flexibility and control over their learning experience.
+ - No perceived limitations: Users are less likely to encounter restrictions or frustrations, making the experience more intuitive.
+
+ * Cons:
+ - More error-prone: Allowing all commands may lead to unexpected bugs during a review session.
+
+### Yes and No
+
+#### **Feature Overview**
+
+The "Yes" and "No" commands feature has been created with the intention of enabling users to provide feedback during the review session. It empowers users to share their opinions regarding whether they have successfully committed the words to memory or not. Following the receipt of this feedback, the application will adjust the proficiency level of the specific word in question. The greater the number of times a user memorizes a word, the higher its proficiency level will rise, and subsequently, the longer the user will have before revisiting that particular word during future review sessions.
+
+*Usage*
+
+Both commands can be executed with the following command words:
+
+- For indicating successful memorization: `yes`
+- For indicating unsuccessful memorization: `no`
+
+#### **Code Structure**
+
+The YesCommand and NoCommand classes consist of important methods:
+
+- `execute(Model model)`: This method is responsible for executing the command. It updates the model and retrieves the next review word. It returns a `CommandResult` with the success message and, if applicable, the response from the model.
+
+#### **Implementation**
+
+The implementation of the "Yes" and "No" commands invoke the `updateLevel` method of `FlashCard` and `hasNextRound` method of `Model`.
+
+- **updateLevel:** updates the proficiency level of the current word and determines the next review date for the word based on the updated proficiency level.
+- **hasNextRound:** determines whether there are more words to review.
+
+The application recognizes a word as memorized if the user inputs either 'yes' or 'no.'
+
+It's important to note that the 'yes' and 'no' commands are only functional once the review session has commenced.
+
+Given below is an example usage scenario and how the yes/no mechanism behaves at each step.
+
+**Step 1:** The user initiates the review session, and a single word appears on the screen without its translation.
+
+**Step 2:** The user recalls the translation from memory and compares it with the provided translation.
+
+**Step 3:** The user responds with either 'yes' or 'no' to indicate whether the recalled translation matches the recorded one.
+
+**Step 4:** If 'yes' is chosen, the `updateLevel` method within the `FlashCard` class is invoked with the 'true' parameter; if 'no' is chosen, it is invoked with 'false'.
+
+**Step 5:** Inside the `updateLevel` method, the proficiency level of the flash card is updated, and the next review date for the word is determined based on the updated proficiency level.
+
+**Step 6:** The `hasNextRound` method within the `Model` class is invoked to determine whether there are more words to review.
+
+**Step 7:** If there are more words to review, the next word is retrieved from the model and displayed on the screen. If there are no more words to review, the review session is concluded.
+
+**Step 8:** The user repeats steps 2-7 until the review session is concluded.
+
+The following sequence diagram summarizes the workflow when a user executes a `yes` command:
+
+
+![YesSequenceDiagram](images/YesSequenceDiagram.png)
+
+**User Control:**
+- User can update the proficiency level of every individual word and also the revisited date, which allows the user to learn in a more targeted manner
+
+The following activity diagram summarizes what happens when a user executes a `yes` command:
+
+![YesCommandActivityDiagram](images/YesCommandActivityDiagram.png)
+#### **Usage Example**
+To use the YesCommand, simply type yes during a review session. For example:
+
+- `yes`
+- `no`
+
+#### **Design Considerations**
+
+**Aspect: How to make our programs more interactive**
+* Alternative 1 (current choice): Let the user judge whether he has remembered the word.
+ * Pros: Get real-time feedback from users and adjust the proficiency of each word.
+ * Cons: Users may trick the program to achieve higher accuracy.
+
+* Alternative 2: Let the user enter the translation he recalls and the program compares it with the recorded translation.
+ * Pros: Get real reactions from users.
+ * Cons: It is difficult for the program to determine whether the meaning expressed by the two translations is consistent
+
+### Light and Dark Mode
+
+#### Implementation
+
+The preference for light and dark themes is stored in the `UserPrefs` class. The `UserPrefs` is initialized by interacting
+with `UserPrefsStorage` when the application is launched. Then `UI` component will obtain the preference from `Logic`
+component and set the initial theme. After a `SwitchCommand` is executed by the `LogicManager`, `Model` component will
+update the theme in `UserPrefs`. Finally, `UI` component will update the theme accordingly.
+
+**Step 1: Theme initialization**
+
+Similar to GUI settings, the theme is regarded as a component of user preference stored in `UserPrefs` and in Json
+file `preferences.json`.
+
+The initial theme setting works as follows: After constructing the `ModelManager` and `LogicManager` with the
+loaded `UserPrefs`, `MainWindow` will obtain theme preference from `LogicManager` and set the initial theme. If no data
+can be read from the preference file, the _Default_ theme will be used by `Logic` and `Model` components, and set by
+`UI`.
+
+**Step 2: Theme switching**
+
+The following sequence diagram shows how the theme switching works. For the discussion purpose, parsing of the command
+and `Storage#saveFlashlingo(ReadOnlyFlashlingo)` are omitted:
+
+![SwitchSequenceModel](images/SwitchSequenceDiagram.png)
+
+#### Design Considerations
+
+**Aspect: How to update UI changes after command execution**
+* **Alternative 1 (current choice): Use `Logic` component to update `Model` and `Storage`.** Add a boolean field `switchTheme`
+ in `CommandResult`, informing `UI` to update similarly to what we did in _help_ and _exit_ commands.
+ * Pros: Follows the separation of concerns principle. Each component is responsible for its own work and addresses
+ separate concerns, achieving higher cohesion and lower coupling.
+ * Cons:
+ * The abstraction and division for different components may be complicated and hard to understand.
+ * Additional field needed in `CommandResult` class.
-**Aspect: How undo & redo executes:**
+* **Alternative 2: Let `UI` component update the theme directly after receiving the command.**
+ * Pros: More direct implementation design.
+ * Cons:
+ * Needs to include more information returned from the execution of command.
+ * A potential gap between current storage and UI theme setting would occur since `UI` wouldn't rely on `Logic` component to update the theme.
-* **Alternative 1 (current choice):** Saves the entire address book.
- * Pros: Easy to implement.
- * Cons: May have performance issues in terms of memory usage.
+### Load data from Excel file
-* **Alternative 2:** Individual command knows how to undo/redo by
- itself.
- * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted).
- * Cons: We must ensure that the implementation of each individual command are correct.
+#### Implementation
+The general flow of the `load` command is similar to most other commands, as `model` executes the command to create and add the flash cards, and also update `storage`. Then the `CommandResult` returned will be reflected in `ui`.
+Due to the complexity of inputs in Excel file, it's crucial to ensure the success of reading the Excel file, actively communicating with `storage` and handling different types of errors.
-_{more aspects and alternatives to be added}_
+**Highlight 1: Transferring Excel input to `FlashCard` object**
-### \[Proposed\] Data archiving
+* The third-party library _**Apache POI OOXML**_ is utilized to read the workbook, sheet to cells in the file. Since Excel is hierarchically composed of smaller elements such as cell, row and column to bigger ones like sheet, workbook, it's necessary to first understand this structure and foresee issues with each layer thoroughly.
+* `DataFormatter` is used to transfer value in the cell to `String`, which avoids issues caused by numerical input read from Excel.
+* Help function `trimAndVerifyWords` ensures the correctness of every word/translation. Once it detects invalid input, it throws `CommandException` and ends the loading immediately, with no further time and resources wasted.
+
+**Highlight 2: Classifying different types of errors**
+
+The activity diagram below illustrates the flow after calling the `LoadCommand#execute(Model)`. **Four** different error messages can be output after execution.
+
+![LoadCommandActivityDiagram](images/LoadCommandActivityDiagram.png)
+
+![img](images/ReadRowsActivityDiagram.png)
+
+**Highlight 3: handling duplicate cards within the file/with the app**
+1. The method `addFlashCardsToModel` takes in the temporarily built flash cards list and the `model` to check if duplicate cards exist in the temporary list.
+2. After passing the check, `Model#addFlashCards(ArrayList)` double checks every input card and ensures it is not the same with existing cards in `model` before adding, otherwise the repeated card will be ignored.
+
+#### Design Considerations
+**Aspect: Accepted file type for loading the data**
+* **Alternative 1 (current choice): Use Excel as the input file type.**
+ * Pros:
+ * The cell-based structure of Excel fits with our word and translation structure of the flash cards, thus it's the most convenient way for users to create and edit data.
+ * It's a common file type that is familiar to most users.
+ * Cons: Need to use external library to read and handle Excel file.
+* **Alternative 2: Use JSON as the input file type.**
+ * Pros: Similar data loading exists in current project, thus easy to implement.
+ * Cons:
+ * Users need to follow the specific format to create and edit Json that may not be familiar to them. This process can be error-prone.
+ * The feature is redundant since advanced users can achieve the same thing by manually adding flash cards to `flashlingo.json`.
+* **Alternative 3: Use TXT, Word or other file type.**
+ * Pros: Introduces a new way for users who prefer using text file.
+ * Cons: Need to use external library to read and handle the unformatted, more complicated input.
+
+**Aspect: How to handle duplicate flash cards**
+
+There are two situations that need to be considered: duplicate flash cards **within the file** and **with the card existing in the app**.
+* **Alternative 1 (current choice): Check together and "ignore".** Check all loaded flash cards to ensure no duplicate with existing cards in `model`, then check their occurrence in `model` again when adding them one by one. **By ignoring the repeated ones, duplicate cards within the file can be handled.**
+ * Pros:
+ * Consistent with the role of `Model` and `Logic`.
+ * Existing checker `Model#hasFlashCard(FlashCard)` can be reused.
+ * Cons: Complexity of flow and logic increases.
+* **Alternative 2: Check the two situations separately.**
+ * Pros: Simpler logic and implementation effort required.
+ * Cons: Heavier time and space usage.
-_{Explain here how the data archiving feature will be implemented}_
--------------------------------------------------------------------------------------------------------------------
@@ -257,13 +481,19 @@ _{Explain here how the data archiving feature will be implemented}_
**Target user profile**:
-* has a need to manage a significant number of contacts
+* language learners, focus on beginner language learners
+* need to manage a significant number of vocabularies
+* need to learn and remember words by flash cards
+* need to review learned vocabularies by scheduled plans
* prefer desktop apps over other types
* can type fast
* prefers typing to mouse interactions
* is reasonably comfortable using CLI apps
-**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
+
+**Value proposition**:
+
+Our product empowers users to efficiently manage their vocabulary through flash cards and seamlessly review the words in review sessions. With Flashlingo, language learners can master new words scientifically while staying in control of their customized language learning journey.
### User stories
@@ -272,106 +502,446 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
| Priority | As a … | I want to … | So that I can… |
| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- |
-| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App |
-| `* * *` | user | add a new person | |
-| `* * *` | user | delete a person | remove entries that I no longer need |
-| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list |
-| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident |
-| `*` | user with many persons in the address book | sort persons by name | locate a person easily |
-
-*{More to be added}*
+| `* * *` | language learner | add new flashcards | |
+| `* * *` | language learner | delete a flashcard | |
+| `* * *` | language learner | list all my flashcards | see what words are currently saved |
+| `* * *` | language learner | save my list of flashcards | keep my progress of my words and the flashcards without having to re-input |
+| `* * *` | language learner | load my flashcards | continue my progress from my last save point |
+| `* * *` | language learner | flip over a flashcard | reveal the translated word to remember |
+| `* * *` | forgetful language learner | be shown the flashcards to see | go through the required flashcards without needing to keep track of what needs to be read |
+| `* *` | not tech saavy language learner | be directed to the help page easily | use the application correctly |
+| `* *` | langauge learner | see my success rate for each flashcard | see what words are challenging |
+| `*` | multi language learner | categorise flashcards by language | study each language individually |
+| `*` | lazy language learner | get translations for the original word | add words in easily without manually searching for the translation |
### Use cases
-(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise)
+**System:** Flashlingo\
+**Use case:** UC1 - Help\
+**Actor:** User\
+**MSS:**
+
+1. User requests help by either entering the help command in the command box or clicking the Help Button on the Flashlingo interface.
+2. Flashlingo opens the default web browser and navigates to the User Guide page.
+
+Use case ends.
+
+**System:** Flashlingo\
+**Use case:** UC2 – Add a word\
+**Actor:** User\
+**MSS:**
+1. User chooses to add a word and its translation by keying in command.
+2. Flashlingo adds the word and its translation.\
+ Use case ends.
+
+**Extensions:**\
+1a. User adds word and translation and specifies the language of the original word and translation.\
+1a1. Flashlingo adds the word and its translation as well as the language of both.\
+Use case ends.
+
+
+**System:** Flashlingo\
+**Use case:** UC3 – Delete a word\
+**Actor:** User\
+**MSS:**
+1. User chooses to delete a word by keying in command
+2. Flashlingo deletes the word and its translation.\
+ Use case ends.
+
+**System:** Flashlingo\
+**Use case:** UC4 – Display list of flashcards\
+**Actor:** User\
+**MSS:**
+1. User chooses to display the list of flashcards.
+2. Flashlingo displays list of cards with words and corresponding translations.\
+ Use case ends.
+
+**System:** Flashlingo\
+**Use case:** UC5 – Start today’s flashcard sequence\
+**Actor:** User\
+**MSS:**
+1. User chooses to start.
+2. Flashlingo displays the words user is going to study.\
+ Use case ends.
+
+**System:** Flashlingo\
+**Use case:** UC6 – Display translation on the other side of flashcard\
+**Actor:** User\
+**MSS:**
+1. User chooses to flip the flashcard
+2. Flashlingo shows meaning of the word.\
+ Use case ends.
+
+**System:** Flashlingo\
+**Use case:** UC7 – Indicate user has remembered word\
+**Actor:** User\
+**MSS:**
+1. User confirms remembrance of the word.
+2. Flashlingo increments level of the flashcard.
+3. Flashlingo displays congratulatory message.\
+ Use case ends.
+
+**Extensions:**\
+2a. Flashlingo detects that level of flashcard exceeds threshold\
+2a1. Flashlingo deletes the flashcard.\
+Use case resumes from step 3.
+
+**System:** Flashlingo\
+**Use case:** UC8 – Indicate user has forgotten word\
+**Actor:** User\
+**MSS:**
+1. User indicates they couldn’t remember word.
+2. Flashlingo decements level of flashcard.
+3. Flashlingo displays motivational message to keep up.\
+ Use case ends.
+
+**Extensions:**\
+2a. Flashlingo detects that level of flashcard is at base level of 1\
+2a1. Flashlingo does not decrement any further, leaving level at 1.\
+Use case resumes from step 3.
+
+**System:** Flashlingo\
+**Use case:** UC9 – Stop session\
+**Actor:** User\
+**MSS:**
+1. User chooses to stop session.
+2. Flashlingo stops and displays the completion message.\
+ Use case ends.
+
+**System:** Flashlingo\
+**Use case:** UC10 – Exit the platform\
+**Actor:** User\
+**MSS:**
+1. User chooses to exit
+2. Flashlingo closes GUI and terminates.\
+ Use case ends.
+
+**System:** Flashlingo\
+**Use case:** UC11 – Change data source\
+**Actor:** User\
+**MSS:**
+1. User chooses to change data source by adding new file-path.
+2. Flashlingo changes data source and displays success message.\
+ Use case ends.
+
+**System:** Flashlingo\
+**Use case:** UC12 – Load data source\
+**Actor:** user\
+**MSS:**
+1. User chooses to load a data source at input file-path.
+2. Flashlingo loads data source and displays success or failure message.\
+ Use case ends.
-**Use case: Delete a person**
+### Non-Functional Requirements
-**MSS**
+1. **Environment** - Should work on any _mainstream OS_ as long as it has Java `11` installed.
+2. **Environment** - Should be able to store 300 flash cards with less than 30MB storage.
+3. **Performance** - A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
+4. **Performance** - Should be able to handle most of the user input within 2 seconds.
+5. **Quality** - Should be able to update already memorized words accordingly and maintain the left ones when a learning session accidentally closes.
+6. **Quality** - Should be able to provide the learner with a reasonable time schedule for language learning.
+7. **Quality** - Should be able to handle any user input correctly without crashing.
+8. **Capacity** - Should be able to hold up to 100 flash cards without a noticeable sluggishness (longer than 2 seconds) in performance for typical usage.
-1. User requests to list persons
-2. AddressBook shows a list of persons
-3. User requests to delete a specific person in the list
-4. AddressBook deletes the person
+### Glossary
- Use case ends.
+* **Mainstream OS**: Windows, Linux, Unix, macOS
+* **Proficiency level**: A number that indicates how well a user knows a word. The higher the number, the better the user knows the word.
+* **Flashcard**: A virtual card with a word on one side and its translation on the other side
+* **Word**: A word in the language you want to learn
+* **Translation**: The word in your native language that corresponds to the word you want to learn
+* **Review Session**: A review session is a designated phase within the system where words requiring review are presented individually. Users can confirm their understanding or signal a need for further review.
-**Extensions**
+--------------------------------------------------------------------------------------------------------------------
-* 2a. The list is empty.
+## **Appendix: Planned Enhancement**
- Use case ends.
+### Enhancement 1: Improving UI
-* 3a. The given index is invalid.
+**Feature Flaw**
- * 3a1. AddressBook shows an error message.
+* Users are not able to see the words clearly as the current font size is too small.
+* It is tedious for users to scroll down or right to view the whole error message or tip as the current output font size is too big.
+* Users now are not able to check the translation language unless they enter the `reveal` command. This is not user-friendly as users may want to know the translation language before revealing the translation.
- Use case resumes at step 2.
+**Proposed Enhancement**
+* Increasing the font size of the words and translations in the flash cards.
+* Decreasing the font size of the output messages.
+* Adding language tag under the `level` tag on each flash card. This will allow users to distinguish different languages of the translations before calling the `reveal` command.
-*{More to be added}*
+**Sample UI**
-### Non-Functional Requirements
+| Flash Card | ![img.png](images/LanguageTag.png) |
+|--------------------|----------------------------------------------|
+| **Output Display** | ![img.png](images/OutputWithSmallerFont.png) |
-1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed.
-2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
-3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
+### Enhancement 2: Improving the content of output messages
-*{More to be added}*
+**Feature Flaw**
-### Glossary
+Some of our current output messages are not formatted properly. For example, the message entering the `reveal` command is "Flashcard has been revealed!" followed directly by the translation. Users may be confused to distinguish the message and the translation.
-* **Mainstream OS**: Windows, Linux, Unix, OS-X
-* **Private contact detail**: A contact detail that is not meant to be shared with others
+**Proposed Enhancement**
+* Formatting the messages to be more clear and consistent with User Guide.
+* Specifying output messages to be more detailed and grammatically correct.
---------------------------------------------------------------------------------------------------------------------
+**Sample Output**
-## **Appendix: Instructions for manual testing**
+| Current Output Message | After Enhancement |
+|--------------------------------------|---------------------------|
+| ![img.png](images/FindMessage.png) | `` |
+| ![img.png](images/ReviewMessage.png) | ...1 flashcard(s) listed! |
-Given below are instructions to test the app manually.
+### Enhancement 3: Adding frequency of usage tags
-
:information_source: **Note:** These instructions only provide a starting point for testers to work on;
-testers are expected to do more *exploratory* testing.
+**Feature Flaw**
-
+Currently, our application does not include functionality for categorizing flashcards with tags, such as 'Essentials', 'Uncommon', 'Rare', and 'Slang'. This omission can lead to a less structured learning experience, as users are unable to sort or prioritize flashcards based on the frequency and context of word usage. Without this feature, users might find it challenging to focus their study on the most pertinent words, potentially impacting the efficiency and effectiveness of language learning.
-### Launch and shutdown
+**Proposed Enhancement**
+* Introduce a flexible tagging system allowing users to add tags to flashcards.
+* These tags could include categories like 'Essentials', 'Uncommon', 'Rare', and 'Slang'.
-1. Initial launch
+*Usage*
- 1. Download the jar file and copy into an empty folder
+Tags can be added or modified using the following commands:
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
+* To add a tag: `addt`
+* To edit a tag: `editTag`
+* To delete a tag: `delTag`
-1. Saving window preferences
+**Implementation:**
- 1. Resize the window to an optimum size. Move the window to a different location. Close the window.
+The `AddTagCommand`, `EditTagCommand`, and `DeleteTagCommand` will operate under a similar framework, ensuring a consistent user experience across different tag-related functionalities. While each command serves a distinct purpose—adding, modifying, or removing tags—their underlying mechanisms share a common structure.
- 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained.
+The `AddTag` command will invoke the `setTags()` of `FlashCard`
+* **setTags:** Adds the passed tags to the current FlashCard's set of Tags
+
+The `EditTag` command will invoke the `replaceTags()` method of `FlashCard`
+* **replaceTag:** Replaces a specified Tag from this FlashCard's Set of Tags with a new Tag
+
+The `DeleteTag` command will invoke the `deletetTags()` method of `FlashCard`
+* **deleteTag:** Deletes the specified Tag from this FlashCard's Set of Tags
+
+All the Tag Commands will also invoke the `updateFilteredFlashCardList()` of `Model`
+* **updateFilteredFlashCardList:** Updates the list of FlashCards currently being displayed to the user
+
+The process of adding tags to a flashcard through the AddTagCommand would be as follows:
+
+**Step 1**: The user identifies the flashcard to be tagged by its index in the list.
+
+**Step 2**: The user specifies the tag(s) to be added to this flashcard. This is done by using the command `addt` followed by the `INDEX` and the `TAG` parameter.
+
+**Step 3**: Upon executing the command, the application retrieves the flashcard from the list based on the provided index.
+
+**Step 4**: The specified tags are added to the flashcard. If any tag already exists, it will not be duplicated.
+
+**Step 5**: The application updates its internal model to reflect these changes. This includes updating the list of flashcards to ensure the newly tagged flashcard is accurately represented in the user interface.
+
+**Step 6**: The user receives a confirmation message indicating the successful addition of tags to the flashcard.
-1. _{ more test cases … }_
+The following sequence diagram summarizes the workflow when a user executes a `AddTag` command:
-### Deleting a person
+![img.png](images/AddTagsSequenceDiagram.png)
-1. Deleting a person while all persons are being shown
+A similar structure is followed for the `EditTagCommand` and `DeleteTagCommand`, but with the respective methods (`replaceTags` and `deleteTags`)
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
+**Usage Examples**
+The Tag commands can be used in the following ways:
+- `addt 2 tg/Essentials` - Adds the 'Essentials' tag to the flashcard at index 2.
+- `editTag 2 ot/Rare rt/Uncommon` - Changes the 'Rare' tag to 'Uncommon' on the flashcard at index 2.
+- `delTag 3 tg/Essentials` - Deletes the 'Essentials' tag from the flashcard at index 3.
- 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
+**Design Considerations**
- 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+**Aspect: What is the most effective method of tagging Flashcards:**
+In enhancing the functionality of our flashcard system, we considered how users could tag flashcards to optimize their learning experience.
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous.
+* Alternative 1 (current choice): Choosing from a predefined set of tags.
-1. _{ more test cases … }_
+ * Pros:
+ * Ensures consistency and standardization across the tagging system.
+ * Simplifies the user interface and user experience, as users can quickly select from a limited set of options.
+ * Facilitates easier implementation and maintenance from a programming perspective.
+ * Cons:
+ * Limits user flexibility and creativity, as they can only use the tags provided.
+ * May not cover all possible categories or nuances that users might need for their individual learning paths.
+* Alternative 2: Allowing users to create custom tags.
-### Saving data
+ * Pros:
+ * Provides users with complete flexibility to create tags that precisely fit their learning needs and styles.
+ * Encourages users to engage more deeply with the material by thinking critically about how to categorize it.
+ * Cons:
+ * Can lead to a lack of standardization, making it harder to implement features that rely on consistent tagging.
+ * Potentially complicates the user interface and increases the learning curve for new users.
+
+**Aspect: How should the tagging system handle multiple tags per flashcard:**
-1. Dealing with missing/corrupted data files
+* Alternative 1 (current choice): Allow Multiple Tags per Flashcard
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+ * Pros:
+ * Enables more comprehensive categorization of flashcards.
+ * Offers greater flexibility for users to organize their study materials according to diverse criteria.
+ * Cons:
+ * Increases the complexity of the user interface and the backend logic.
+ * May lead to clutter or confusion if not managed effectively by the user.
+* Alternative 2: Limit Each Flashcard to a Single Tag
+
+ * Pros:
+ * Simplifies the interface and makes the system straightforward to use.
+ * Easier to implement and manage from a programming perspective.
+ * Cons:
+ * Reduces the depth of categorization, potentially leading to an overly simplifyied representation of complex language ideas.
+ * Limits the ability to cross-reference flashcards under multiple relevant categories.
+
+### Enhancement 4: Visualization Upgrade for 'Stats' Command
+ **Feature Flaw:**
+ * Currently, the success rate in the 'stats' command is presented as a mere percentage, which might not be the most engaging or insightful way for users to visualize their learning progress.
+ * Users lack a graphical representation of their learning statistics, which can provide a more immediate and comprehensive understanding of their performance.
+
+**Proposed Enhancement:**
+ * Enhance the 'stats' command by including a pie chart along with the percentage. This pie chart will visually represent the proportion of words remembered versus words not remembered.
+ * The pie chart will offer a clearer, more immediate visual representation of the user's success rate, making it easier to grasp their learning progress at a glance.
+
+**Sample Output:**
+
+|Before the update | ![img.png](images/Stats.png) |
+|:-----------------|-------------------:|
+|**After the update**| ![img.png](images/StatsChart.png) |
+
+### Enhancement 5: Providing more error messages when failing to load the data file
+
+**Feature Flaw**
+
+For advanced users who manually edit the data, they may accidentally introduce invalid data into the file. However, they are not able to know the reason why the data file is valid and that all the data will be discarded when the app starts to process any commands.
+
+**Proposed Enhancement**
+* Changing the behavior of `MainApp#initModelManager`, when `DataLoadingException` is thrown, call the `ui` to display the error message and prohibit `model` from executing any commands.
+* Providing more error messages when failing to load the data file.
+
+**Sample Output**
+
+* `Duplicated flash card ... found in flashlingo.json`: Duplicate combination of word and translation is found in the data file.
+* `Invalid flash card ... with invalid ... found in flashlingo.json`: Flash card with invalid word/translation, level or review date found.
+
+### Enhancement 6: Renaming `reveal` command to reduce ambiguity
+
+**Feature Flaw**
+
+Currently, the `reveal` command is used to reveal or hide the translation of the flash card. However, it may be ambiguous to users as they may think that the command is used to reveal the word itself.
+
+**Proposed Enhancement**
+
+Renaming the `reveal` command to `flip` to reduce ambiguity.
+
+
+--------------------------------------------------------------------------------------------------------------------
+
+## **Appendix: Instructions for manual testing**
+
+Given below are instructions to test the app manually.
+
+
:information_source: **Note:** These instructions only provide a starting point for testers to work on;
+testers are expected to do more *exploratory* testing.
+
+
+
+1. Initial launch
+
+ 1. Download the jar file and copy into an empty folder
+
+ 2. Navigate to folder in terminal and input `java -jar` command with the jar file name, e.g.`java -jar flashlingo.jar`
+ Expected: Shows the GUI with a set of sample contacts. The window size can be adjusted to your liking.
+
+2. Saving window preferences
+
+ 1. After resizing and adjusting the window size and location, close the window.
+
+ 2. Re-launch the app through the same method in step 1.
+ Expected: The most recent window size and location is retained.
-1. _{ more test cases … }_
+3. Adding/deleting a flash card
+
+ 1. Try out the `add` command in the command box.
+ Example command:
+ 1. `add w/OriginalTest t/TranslationTest`
+ 2. `add w/OriginalTestWithLanguage t/TranslationTestWithLanguage wl/OriginalLang tl/TranslationLang`
+
+ Expected: A new flash card is added to the flash card list.
+ 2. Execute the `delete` command with the index of the flashcard you just added in the command box.
+ Expected: The flash card added in the previous step is successfully deleted.
+
+4. Editing a flash card
+ 1. Use the add command to add a flash card of your choice
+ 2. Enter the `edit` command in the command box.
+ Example command:
+ 1. `edit 1 w/EditedWord t/EditedTranslation wl/EditedLanguage tl/EditedTranslationLanguage`
+ Feel free to remove the parameters as you wish, as long as any one of them is still used.
+
+ Expected: The flashcard's details will be changed accordingly.
+
+5. Finding a flash card based on keyword
+ 1. Add a few flash cards using the below command
+ 1. `add w/雪 t/snow`
+ 2. `add w/snow t/nieve wl/English tl/Spanish`
+ 3. `add w/ciao t/hello/goodbye`
+ 2. Enter the `find` command in the command box.
+ Example command:
+ 1. `find snow`
+ Expected: Flashcard (a) and (b)
+ 1. `find nieve`
+ Expected: Flashcard (b)
+ 1. `find nieve, ciao`
+ Expected: Flashcard (a) and (c)
+ 1. `find snow, hello`
+ Expected: Flashcard (a), (b) and (c)
+
+6. Finding a flash card based on language
+ 1. Add a few flash cards using the below command (unless already added in previous sections)
+ 1. `add w/雪 t/snow`
+ 2. `add w/snow t/nieve wl/English tl/Spanish`
+ 3. `add w/ciao t/hello/goodbye`
+ 2. Enter the `language` command in the command box.
+ Example command:
+ 1. `language English`
+ Expected: Flashcard (b)
+ 1. `language Spanish`
+ Expected: Flashcard (b)
+ 1. `language`
+ Expected: Flashcard (a) and (c)
+
+7. Listing all flash cards
+ 1. Assuming that the list is filtered after section 5 and 6
+ 2. Enter the `list` command in the command box.
+ Expected: All flash cards in the list to be displayed
+
+8. See which flash cards require reviewing
+ 1. Enter the `review` command in the command box
+ Expected: The displayed flash cards will be filtered to show only flash cards that require reviewing
+
+9. Start and End the review session
+ 1. Assuming you are not currently in a review session and there are flash cards yet to be reviewed
+ 2. Enter the `start` command in the command box.
+ Expected: Log message to be "Review session has been started" and one flash card displayed
+ 3. Enter the `end` command in the command box to terminate the review session
+ Expected: Log message to be "Review session has ended" and display goes back to main menu.
+
+10. Reviewing the words (Yes/No) and revealing the word
+ * Before starting the review session, take note of the current levels of the flash cards to verify if levels have been incremented or decremented correctly
+ 1. Enter the `start` command in the command box to begin the review session.
+ 2. One flash card should show up. To check if you got the word correctly, either type in `reveal` or press the Reveal button.
+ 1. If you want to hide the translation, type in `reveal` again or press the Hide button.
+ 2. To confirm that you have remembered the word, type in `yes` or press the Yes button.
+ 1. After exiting the review session, confirm that the level on the flash card had been increased by 1, or became "Word Mastered" if the level passes the threshold of 5
+ 3. If you had failed to remember, type in `no` or press the No button
+ 1. After exiting the review session, confirm that the level on the flash card had been decreased by 1, or stayed at 1 if its original level is 1.
+
+
+11. Loading words from excel file into app
+ * You can download a sample file from [here](SampleData.xlsx)
+ 1. Enter the `load SampleData.xlsx` command in the command box after moving it to the folder containing the jar file.
+ Expected: The words from the excel sheet will be added to the current list of flash cards
+
+12. Switching the theme of the application
+ 1. Enter the `switch` command in the command box.
+ 2. Expected: If on light mode, will switch to dark mode, otherwise switch to light mode
diff --git a/docs/SampleData.xlsx b/docs/SampleData.xlsx
new file mode 100644
index 00000000000..a92279a399e
Binary files /dev/null and b/docs/SampleData.xlsx differ
diff --git a/docs/SettingUp.md b/docs/SettingUp.md
index 275445bd551..f5101c298b0 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 `seedu.flashlingo.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..610dda0b614 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. `seedu.flashlingo.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. `seedu.flashlingo.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. `seedu.flashlingo.logic.LogicManagerTest`
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 57437026c7b..bf8910c0ae0 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -3,195 +3,504 @@ layout: page
title: User Guide
---
-AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps.
-
* Table of Contents
{:toc}
--------------------------------------------------------------------------------------------------------------------
-## Quick start
+## Introduction
+Flashlingo is a versatile desktop application centered around learning words through flash cards. It is optimized for use via a **Command Line Interface** (CLI),
+while also providing the advantages of a **Graphical User Interface** (GUI). Tailored with a focus on **beginner language
+learners** with beginner level of proficiency in command line, Flashlingo specializes in expanding vocabulary. If you are not familiar with the command line interface (CLI), you can refer to the [**Glossary**](#glossary) section below,
+and the [**Command Summary**](#command-summary) section for a quick overview of the commands.
-1. Ensure you have Java `11` or above installed in your Computer.
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+By incorporating [**Leitner system**](https://en.wikipedia.org/wiki/Leitner_system#), Flashlingo schedules review sessions, ensuring words are revisited at
+optimal intervals to enhance long-term memory retention. This method assists users in effectively retaining and expanding their vocabulary over time.
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+--------------------------------------------------------------------------------------------------------------------
-1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png)
+## Application Features
+Flashlingo predominantly consists of two main features: **Managing flash cards** and **Reviewing flash cards**
+- **Managing flash cards**
+ - Users are given the ability to add, delete and edit flash cards. And each flash card is assigned with a `level`, which
+ indicates the proficiency of the user with the word. The level of the flash card will be updated after each review session.
+* **Reviewing flash cards**
+ - In each review session, words that require reviewing will be presented to users one by one. For each word, users can indicate whether they have
+ - memorized the word
+
+ This will advance the word into the next `level`, meaning less frequent review for the word in the near future.
+ - forgotten the word
+
+ This will decrease the `level` by 1, meaning more frequent review for the word in the near future.
-1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try:
+
- * `list` : Lists all contacts.
+**:information_source: Notes about the level:**
- * `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book.
+* The higher the `Level` is, the higher the proficiency of the user with the word is. The maximum level is 5, and it will be displayed as `word mastered`.
- * `delete 3` : Deletes the 3rd contact shown in the current list.
+* Words which are indicated as `word mastered` will not appear in review session.
- * `clear` : Deletes all contacts.
+
- * `exit` : Exits the app.
+With each review frequency scientifically determined by **Flashlingo**, it will make your vocabulary learning an absolute breeze,
+meaning you can **spend less time and memorize things more enduringly.**
-1. Refer to the [Features](#features) below for details of each command.
--------------------------------------------------------------------------------------------------------------------
-## Features
+## Quick start
+
+1. Ensure you have Java `11` installed in your computer.
+
+2. Download the latest `flashlingo.jar` from [here](https://github.com/AY2324S1-CS2103T-T11-4/tp/releases).
+
+3. Copy the file to the folder you want to use as the _home folder_ for your Flashlingo.
+
+4. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar flashlingo.jar` command to run the application.
+ Below shows the steps to perform such a task
+ 1. Open up the terminal.
+ * For mac users, press `Command + Space` to open Spotlight search, type `Terminal`, and press `Enter`.
+ ![img.png](images/Terminal.png)
+ * For windows users, press `Windows + R`, type `cmd` and press `Enter`.
+ 2. Navigate to the folder containing the jar file. In this example, it is in the Downloads folder. So the command is:
+ ```
+ cd Downloads
+ ```
+ >Click [here](https://en.wikipedia.org/wiki/Cd_(command)) for more information on how to navigate to a folder in the terminal.
+ 3. Simply type in `java -jar flashlingo.jar` to get started!
+
+ A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
+ ![Ui](images/MainUi.png)
+
+### Using the application
+
+1. Type the command in the command box and press Enter to execute it.
+
+
+2. The image below shows the result of typing in a command. A log message will be displayed below the command box to give information about the outcome of the command.
+
+
+3. Refer to the [Commands](#commands) below for details of each command.
+
+
+--------------------------------------------------------------------------------------------------------------------
+
+## Commands
**:information_source: Notes about the command format:**
-* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`.
+| Notation | Description | Example Command | Example Usage |
+|------------------|---------------------------------------------------------------|-------------------------------------------------------------------------|---------------------------------|
+| `` | Parameter to be supplied by the user | `delete ` | `delete 1` |
+| `[]` | Indicates an optional parameter | `add w/ t/ [wl/] [tl/]` | `add w/Hello t/你好` |
+ | `` | Commands that can take in multiple comma separated parameters | `find ` | `find hello, bye` |
+| `...` | Parameter that will not be used | `help ...` | `help 123` is the same as `help` |
+
+**:information_source: Notes about the command output:**
+`...` in the command output shown below depends on user input and details of the existing flash cards.
+
+
+
+
+| :warning: Commands Supported Only Outside Review Session | :warning: Commands Supported Only Inside Review Session |
+|--------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------|
+| `add` `delete` `edit` `find` `start` `list` `load` `language` `review` `stats` | `yes` `no` `end` |
+| *Error Message:* `Sorry, currently you are in a review session. Your command is not supported. Please end the review session first.` | *Error Message:* `You are not in a review session.` |
-* Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`.
-* Items with `…` after them can be used multiple times including zero times.
- e.g. `[t/TAG]…` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
+### Adding a flash card: `add`
-* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable.
+
-* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
- e.g. if the command specifies `help 123`, it will be interpreted as `help`.
+**:information_source: Notes about adding words:**
+* In order to give users full customizability, there's no any restriction on input, as long as it is not empty.
-* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
-### Viewing help : `help`
+Adds a word to the flash card with its translation.
+* Creates a wild flash card.
+* Works to add a word with its translation in their respective languages.
+* The already saved translation can be overridden with a new translation in a different language.
-Shows a message explaning how to access the help page.
+[Command Format](#commands): `add w/ t/ [wl/] [tl/]`
+> The default language for both word and translation is blank (`""`)
-![help message](images/helpMessage.png)
+Examples:
+* `add w/雪 t/snow` saves the translation of **雪** as **snow**
+* `add w/雪 wl/Chinese t/snow tl/English` saves the translation of the **Chinese** word **雪** as an **English** word **snow**
-Format: `help`
+Output:
+![img.png](images/AddCommand.png)
-### Adding a person: `add`
+**Note**
+* Flash cards are case-insensitive. The following commands are the same:
+ * `add w/CIAO t/hello/bye`
+ * `add w/ciao t/HELLO/BYE`
+* Users can add two flash cards with the **same word and translation BUT in different language**
+ The following three commands will not cause duplicate error:
+ * `add w/雪 t/snow`
+ * `add w/雪 t/snow wl/Chinese tl/English`
+ * `add w/雪 t/snow wl/Japanese tl/English`
+* Users are **not allowed** to add a flash card with the **same word and translation**
+ The following command will cause duplicate error:
+ * `add w/sorry t/sorry`
+ * `add w/sorry t/SORRY`
+* Users are **not allowed** to add a flash card with the **empty word or translation**
-Adds a person to the address book.
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
+### Deleting a flash card: `delete`
-
:bulb: **Tip:**
-A person can have any number of tags (including 0)
-
+Deletes a flash card and its related information
+* Deletes a flash card.
+
+[Command Format](#commands): `delete `
Examples:
-* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
+* `delete 2` deletes the word and its translation at index 2
-### Listing all persons : `list`
+Output:
-Shows a list of all persons in the address book.
+| Before delete | ![img.png](images/BeforeDelete.png) |
+|:----------------:|:-----------------------------------:|
+| **After delete** | ![img.png](images/AfterDelete.png) |
-Format: `list`
+### Editing a flash card: `edit`
-### Editing a person : `edit`
+Edits the specified flash card at the given index.
-Edits an existing person in the address book.
+[Command Format](#commands): `edit [w/] [t/] [wl/] [tl/]`
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+Examples:
+* `edit 1 w/こんにちわ t/Hello` edits the translation at index 1
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
-* At least one of the optional fields must be provided.
-* Existing values will be updated to the input values.
-* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
-* You can remove all the person’s tags by typing `t/` without
- specifying any tags after it.
+Output:
-Examples:
-* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively.
-* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags.
+| Before edit |![img.png](images/BeforeEdit.png) |
+|:--------------:|:---------------------------------:|
+| **After edit** | ![img.png](images/AfterEdit.png) |
+
+**Note**
+* `` is the index of each flash card in the `list`
+* Users must change at **LEAST** one certain parameter by using certain prefix
+ * Word: `w/WORD`
+ * Word Language: `wl/WORD_LANGUAGE`
+ * Translation: `t/TRANSLATION`
+ * Translation Language: `tl/TRANSLATION_LANGUAGE`
+* Users are not allowed to edit a flash card to an existing flash card
+> The error message:
+> `This flash card already exists in Flashlingo!`
+
+### Listing all flash cards : `list`
+
+Shows the list of flash cards with both the original word and the corresponding translation.
+* Lists all the flash cards saved
+
+[Command Format](#commands): `list ...`
+> All the saved flash cards, regardless of the review date, are listed.
+
+Output:
+
+![img.png](images/ListCommand.png)
-### Locating persons by name: `find`
-Finds persons whose names contain any of the given keywords.
+**Note**
+* `list` command cannot be used during a review session
+ * To facilitate learning, only the flash cards - that are to be reviewed - can be seen during the review session.
+ * As soon as the review session ends, all the flash cards can be listed once again.
-Format: `find KEYWORD [MORE_KEYWORDS]`
+### Finding a flash card: `find`
-* The search is case-insensitive. e.g `hans` will match `Hans`
-* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-* Only the name is searched.
-* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search).
- e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+Filters the flash card list by the given keywords. All the flash cards whose word or translation contains at least one of the keywords will be displayed.
+* The search is case-insensitive. e.g `food` will match `Food`
+
+[Command Format](#commands): `find `
Examples:
-* `find John` returns `john` and `John Doe`
-* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png)
+* `find food` returns the flash card list and its translation that contains the keyword `food`
+* `find oo` returns the flash card list and its translation that contains the substring `oo`
+* `find food, bye` returns the flash card list and its translation that contains the keyword `food` or `bye`
+
+Output:
-### Deleting a person : `delete`
+| **Before find** | ![img.png](images/BeforeFind.png) |
+|:---------------:|:---------------------------------:|
+| **After find** | ![img.png](images/AfterFind.png) |
-Deletes the specified person from the address book.
+### Filtering list with specified language: `language`
-Format: `delete INDEX`
+Filters the list of flash cards by the specified language. All the flash cards whose word or translation is in the specified language will be displayed.
+* The search is case-insensitive. e.g `french` will match `French`
-* Deletes the person at the specified `INDEX`.
-* The index refers to the index number shown in the displayed person list.
-* The index **must be a positive integer** 1, 2, 3, …
+[Command Format](#commands): `language []`
Examples:
-* `list` followed by `delete 2` deletes the 2nd person in the address book.
-* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
+* `language French` displays a list where each word or translation is from French language.
+
+**Note**
+* Unlike the `find` command, the `language` command uses **exact search**. So it will not display the flash cards whose word or translation contains the specified language as a substring or keyword.
+ * e.g. `language Chinese` will not display following flash cards:
+ * `w/读书 t/read book wl/简体中文 tl/English`
+ * `w/讀書 t/read book wl/Traditional Chinese tl/English`
+* `language` without any parameter will display all the flash cards with default language `""`.
+
+### Getting list for revision: `review`
+
+Displays the flash cards of all the words to be reviewed that day
+* The review command will present flash cards selected by Flashlingo based on your level, as well as the interval between the last review, utilizing the Leitner system.
+* If you wish to view all your saved flash cards without the [Leitner system's](https://en.wikipedia.org/wiki/Leitner_system#) selection criteria, please use the `list` command.
+
+[Command Format](#commands): `review ...`
+
+Output:
-### Clearing all entries : `clear`
+![img.png](images/ReviewSuccess.png)
-Clears all entries from the address book.
+**Note**
+* The message `0 flashcards listed!` occurs when:
+ * There are no flash cards scheduled for review at the time when the command is executed.
-Format: `clear`
+### Starting a new review session : `start`
-### Exiting the program : `exit`
+Starts a new review session.
-Exits the program.
+[Command Format](#commands): `start ...`
+
+Output: `Review Session has been started.`
+
+**Note**
+* If there are no words to review, users will not be able to start review session. `You have no more words to review!`
+ will be displayed.
+
+
+### Ending the current review session: `end`
+
+Ends the current review session and returns to the main menu.
+
+[Command Format](#commands): `end ...`
+
+Output: `Review Session has ended.`
+
+### Revealing the other side of the flash card: `reveal`
+
+Shows the translation of the flash card given by index if it is hidden. Otherwise, hides the translation of the flash card.
+
+[Command Format](#commands): `reveal []`
+
+Output :
+
+| **If translation is hidden** | ![img.png](images/BeforeReveal.png) |
+|:------------------------------:|:-----------------------------------:|
+| **If translation is revealed** | ![img.png](images/AfterReveal.png) |
+
+
+
+:information_source: Notes:
+
+* When `` is omitted, the default value is 1.
+* Pressing `reveal` or `hide` button will have the same effect.
+* In future enhancement, this command will be renamed to `flip` to better reflect its function.
+
+
+
+### Indicating user has memorized the word: `yes`
+
+Marks the word as memorized and advances the word into the next `level`. If there are still remaining words to review,
+they will be automatically shown in the section below. Otherwise, review session will be closed by default.
+
+[Command Format](#commands): `yes ...`
+
+Output:
+
+![img.png](images/Yes.png)
+
+**Note**
+* Pressing `yes` button will have the same effect.
+* In the case where there are no more words to review, `The next word is:` will be replaced by
+`You have no more words to review!`.
+
+### Indicating user has forgotten the word: `no`
+
+Marks the word as not grasped and decreases its `level` by 1. If there are still remaining words to review,
+they will be automatically shown in the section below. Otherwise, review session will be closed by default.
+
+[Command Format](#commands): `no ...`
+
+Output:
+
+![img.png](images/No.png)
+
+**Note**
+* Pressing `no` button will have the same effect.
+* In the case where there are no more words to review, `The next word is:` will be replaced by
+`You have no more words to review!`.
+
+### Show learning statistics: `stats`
+
+To help track user progress and inspire continued learning, this command offers detailed statistics:
+* Total Flash Cards: Displays the total count of flash cards you have saved.
+* Remembered Words: Shows the number of terms you have successfully retained in this session.
+* Success Rate: Presents a percentage representing your learning success for this session, motivating you to keep improving.
+
+[Command Format](#commands): `stats ...`
+
+Output:
+
+![img.png](images/Stats.png)
+
+**Note**
+* The success rate is calculated solely on the basis of the current session.
+
+
+### Loading list of words: `load`
+Loads an Excel file of words into the app. The words will be added to the current list of flash cards and included in the
+review session automatically.
+
+[Command Format](#commands): `load `
+
+
+
+
+**:information_source: Notes about the file input:**
+* **File format:** The file must be an Excel Workbook with the `.xlsx` extension.
+* **File location:** The file must be located in the **same folder** as the `flashlingo.jar` file.
+* **File name:** The file names, with and without the `.xlsx` extension depend on different systems, should not contain leading or trailing spaces.
+* **File content:** The app only reads the **first two columns** of the **first sheet** of the file.
+ * The sheet must contain only the following two columns (cannot have blank columns on the left):
+ 1. Column A - The original words
+ 2. Column B - The translation of the words
+ * The sheet cannot contain empty rows in between all rows with words.
+* **Resources:** A sample file can be found [here](SampleData.xlsx).
+
+
+
+Output:
+* `You have successfully loaded file: ...`: Successful loading.
+* `File not found or accessible`:
+⚠️ Make sure the file is in the correct directory with read permission.
+* `File cannot be read due to invalid content or format`:
+⚠️ Make sure the file contains only two columns with the valid words/translations.
+* `... flash card already exists!`:
+⚠️ Modify the duplicated word/translation in the file to avoid duplication with the flash cards in the app.
+* `Word/translation cannot be empty!`:
+⚠️ Ensure all words and translations are not empty spaces.
+
+### Switching color theme: `switch`
+Switches between light and dark appearance of Flashlingo.
+* The theme will be saved and loaded when Flashlingo is restarted.
+* Default color theme is the **light theme**.
+
+[Command Format](#commands): `switch ...`
+
+Output:
+
+| Light theme | ![img.png](images/LightTheme.png) |
+|:--------------:|:---------------------------------:|
+| **Dark theme** | ![img.png](images/DarkTheme.png) |
+
+### Viewing help: `help`
+
+Opens a browser with the help page (User Guide). Pressing the `Help` button and then clicking `Help F1` will achieve the same effect.
+
+[Command Format](#commands): `help ...`
+
+### Exiting the program: `exit`
+
+Safely terminates the Flashlingo application and closes the graphical user interface (GUI).
+
+[Command Format](#commands): `exit ...`
-Format: `exit`
### Saving the data
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+Flashlingo data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
### Editing the data file
-AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+Flashlingo data are saved automatically as a JSON file `[JAR file location]/data/flashlingo.json`. Advanced users are welcome to update data directly by editing that data file.
:exclamation: **Caution:**
-If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
+If your changes to the data file make its format invalid, Flashlingo will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
-### Archiving data files `[coming in v2.0]`
-
-_Details coming soon ..._
-
--------------------------------------------------------------------------------------------------------------------
## FAQ
-**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder.
+**Q: How can I check if I have installed and am using the Java `11` version?**
+* Open up the terminal. **Run the `java -version` command.** The output should contain similar information to:
+`version "11.0.X"`.
+* If Java `11` is not installed, you may download it again.
+* If you are using lower or higher versions of Java, you may find switch version on [Mac](https://stackoverflow.com/questions/21964709/how-to-set-or-change-the-default-java-jdk-version-on-macos), [Windows](https://stackoverflow.com/questions/26993101/switching-between-different-jdk-versions-in-windows) or [Linux](https://askubuntu.com/questions/740757/switch-between-multiple-java-versions) useful.
---------------------------------------------------------------------------------------------------------------------
-## Known issues
+**Q: How do I transfer my data to another computer?**
+
+Install the app on the other computer and **overwrite the empty data file** it creates with the **file that contains the data of your previous Flashlingo** home folder.
+
+**Q: How can I import my data to the app?**
+
+Save your words and translations in the specified format in an Excel file. Move the file to the same folder with `flashlingo.jar`.
+Then, **use the `load` command to import the data.**
+
+**Q: What may be the reasons why my data cannot be loaded into the app?**
+
+* First, **check your file name** by opening the located folder. The file name should be directly displayed. Also, you can right-click the file to view the file name in its detailed info.
+* If Flashlingo still cannot read the file, try inputting the file name with and without the extension `.xlsx` in the `load` command. This may solve potential issues with file name loading within different systems.
+* Secondly, ensure the **content in your file is correctly formatted and valid**. All rules can be found [here](#load).
+* There may be issues with loading Excel if you have tried to delete data without using the _delete whole row/column_ function. Copy the entire data (only valid rows and columns) to a new Excel file and try again.
+
+**Q: After I reviewed a flash card, I edited details of the card by the `edit` command. Will I be able to see these changes immediately reflected in the review session?**
+
+* Unfortunately, **no**. The card is updated synchronously in the card list and the review session of Flashlingo. However, since you have already reviewed the same card on the day, the card will not be included in the review session again.
+* To check the card after editing, you can **use the `list` or `find` command** to locate the specific card. The changes will also be reflected in the next review session for that card.
-1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again.
+**Q: If the displayed level of a flash card is `Word Mastered`, what can I do if I want to review the card again?**
+
+* Currently, there's no way for you to manually change the level of a flash card. Flashlingo is designed to automatically remove words that learners are familiar with from the review session.
+* However, if you must review the card again, you can first use the `delete` command to delete the card and then use the `add` command to re-add the same card. The new card will be reset to level 1 and added to the review session.
--------------------------------------------------------------------------------------------------------------------
## Command summary
-Action | Format, Examples
---------|------------------
-**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…` e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
-**Clear** | `clear`
-**Delete** | `delete INDEX` e.g., `delete 3`
-**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…` e.g.,`edit 2 n/James Lee e/jameslee@example.com`
-**Find** | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James Jake`
-**List** | `list`
-**Help** | `help`
+Refer to [Commands](#commands) if unsure of how to interpret the format.
+
+| Action | Format, Examples | Supported Period |
+|-------------------------|----------------------------------------------------------------------------------------------------------------------------|------------------------|
+| **Add** | `add w/ t/ [wl/] [tl/]` e.g., `add w/regarder t/look` | Outside review session |
+| **Delete** | `delete ` e.g., `delete 1` | Outside review session |
+| **Edit** | `edit [w/] [t/] [wl/] [tl/]` e.g., `edit 1 w/bye t/再见` | Outside review session |
+| **Find** | `find ` e.g., `find bye` | Outside review session |
+| **List** | `list ...` | Outside review session |
+| **Review** | `review ...` | Outside review session |
+| **Start** | `start ...` | Outside review session |
+| **End** | `end ...` | Inside review session |
+| **Reveal** | `reveal []` | Both |
+| **Yes** | `yes ...` | Inside review session |
+| **No** | `no ...` | Inside review session |
+| **Learning Statistics** | `stats ...` | Outside review session |
+| **Language** | `language []` e.g., `language French` | Outside review session |
+| **Load** | `load ` e.g., `load SampleData.xlsx` | Outside review session |
+| **Switch** | `switch ...` | Both |
+| **Help** | `help ...` | Outside review session |
+| **Exit** | `exit ...` | Both |
+
+
+--------------------------------------------------------------------------------------------------------------------
+
+## Glossary
+
+| Term | Definition |
+|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **CLI** | A command line interface (CLI) is a text-based interface where you can input commands that interact with a computer's operating system. You can check the tutorial [**here**](https://tutorials.codebar.io/command-line/introduction/tutorial.html). |
+| **GUI** | A graphical user interface (GUI) is a digital interface in which a user interacts with graphical components such as icons, buttons, and menus. |
+| **JSON** | JSON (JavaScript Object Notation) is a lightweight data format commonly used for representing structured data. |
+| **Leitner System** | The Leitner system is a widely used method of efficiently using flashcards that was proposed by the German science journalist Sebastian Leitner in 1972. It is a simple implementation of the principle of spaced repetition, where cards are reviewed at increasing intervals. |
+| **Level** | The level represents the proficiency with a specific flash card. The higher the `Level` is, the higher the proficiency of the user with the word is. |
+| **Review Session** | A review session is a designated phase within the system where words requiring review are presented individually, based on Leitner System. Users can confirm their understanding or signal a need for further review. |
+
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..4a7ad320ceb 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "Flashlingo"
theme: minima
header_pages:
@@ -8,7 +8,7 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+repository: "AY2324S1-CS2103T-T11-4/tp"
github_icon: "images/github-icon.png"
plugins:
diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss
index 0d3f6e80ced..d3edc3e73a5 100644
--- a/docs/_sass/minima/_base.scss
+++ b/docs/_sass/minima/_base.scss
@@ -288,7 +288,7 @@ table {
text-align: center;
}
.site-header:before {
- content: "AB-3";
+ content: "Flashlingo";
font-size: 32px;
}
}
diff --git a/docs/diagrams/AddTagsSequenceDiagram.puml b/docs/diagrams/AddTagsSequenceDiagram.puml
new file mode 100644
index 00000000000..2fefb55eb7a
--- /dev/null
+++ b/docs/diagrams/AddTagsSequenceDiagram.puml
@@ -0,0 +1,49 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":FlashlingoParser" as FlashlingoParser LOGIC_COLOR
+participant ":AddTagCommandParser" as AddTagCommandParser LOGIC_COLOR
+participant ":AddTagCommand" as AddTagCommand LOGIC_COLOR
+participant "s:CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+participant "flashCardToTag:FlashCard" as FlashCard MODEL_COLOR
+end box
+
+[-> LogicManager : execute("addt")
+activate LogicManager
+
+LogicManager -> FlashlingoParser : parseCommand("addt")
+activate FlashlingoParser
+
+FlashlingoParser -> AddTagCommandParser : parse()
+activate AddTagCommandParser
+create AddTagCommand
+AddTagCommandParser -> AddTagCommand : Create instance
+activate AddTagCommand
+
+
+AddTagCommand -> FlashCard: setTags()
+activate FlashCard
+deactivate FlashCard
+AddTagCommand -> Model: updateFilteredFlashCardList()
+activate Model
+deactivate Model
+AddTagCommand -> CommandResult : CommandResult()
+activate CommandResult
+CommandResult --> LogicManager : return CommandResult
+deactivate CommandResult
+deactivate AddTagCommand
+deactivate AddTagCommandParser
+
+
+deactivate FlashlingoParser
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml
index 48b6cc4333c..a15e39f0433 100644
--- a/docs/diagrams/ArchitectureSequenceDiagram.puml
+++ b/docs/diagrams/ArchitectureSequenceDiagram.puml
@@ -14,13 +14,13 @@ activate ui UI_COLOR
ui -[UI_COLOR]> logic : execute("delete 1")
activate logic LOGIC_COLOR
-logic -[LOGIC_COLOR]> model : deletePerson(p)
+logic -[LOGIC_COLOR]> model : deleteFlashCard(f)
activate model MODEL_COLOR
model -[MODEL_COLOR]-> logic
deactivate model
-logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook)
+logic -[LOGIC_COLOR]> storage : saveFlashlingo(flashLingo)
activate storage STORAGE_COLOR
storage -[STORAGE_COLOR]> storage : Save to file
diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml
deleted file mode 100644
index 598474a5c82..00000000000
--- a/docs/diagrams/BetterModelClassDiagram.puml
+++ /dev/null
@@ -1,21 +0,0 @@
-@startuml
-!include style.puml
-skinparam arrowThickness 1.1
-skinparam arrowColor MODEL_COLOR
-skinparam classBackgroundColor MODEL_COLOR
-
-AddressBook *-right-> "1" UniquePersonList
-AddressBook *-right-> "1" UniqueTagList
-UniqueTagList -[hidden]down- UniquePersonList
-UniqueTagList -[hidden]down- UniquePersonList
-
-UniqueTagList -right-> "*" Tag
-UniquePersonList -right-> Person
-
-Person -up-> "*" Tag
-
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
-@enduml
diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml
index 8c0892d6a70..22ae9269d53 100644
--- a/docs/diagrams/CommitActivityDiagram.puml
+++ b/docs/diagrams/CommitActivityDiagram.puml
@@ -8,10 +8,10 @@ start
'Since the beta syntax does not support placing the condition outside the
'diamond we place it as the true branch instead.
-if () then ([command commits AddressBook])
+if () then ([command commits Flashlingo])
:Purge redundant states;
- :Save AddressBook to
- addressBookStateList;
+ :Save Flashlingo to
+ flashlingoStateList;
else ([else])
endif
stop
diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml
index 40ea6c9dc4c..9a7cfab8c33 100644
--- a/docs/diagrams/DeleteSequenceDiagram.puml
+++ b/docs/diagrams/DeleteSequenceDiagram.puml
@@ -4,7 +4,7 @@ skinparam ArrowFontStyle plain
box Logic LOGIC_COLOR_T1
participant ":LogicManager" as LogicManager LOGIC_COLOR
-participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":FlashlingoParser" as FlashlingoParser LOGIC_COLOR
participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR
participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR
participant ":CommandResult" as CommandResult LOGIC_COLOR
@@ -17,17 +17,17 @@ end box
[-> LogicManager : execute("delete 1")
activate LogicManager
-LogicManager -> AddressBookParser : parseCommand("delete 1")
-activate AddressBookParser
+LogicManager -> FlashlingoParser : parseCommand("delete 1")
+activate FlashlingoParser
create DeleteCommandParser
-AddressBookParser -> DeleteCommandParser
+FlashlingoParser -> DeleteCommandParser
activate DeleteCommandParser
-DeleteCommandParser --> AddressBookParser
+DeleteCommandParser --> FlashlingoParser
deactivate DeleteCommandParser
-AddressBookParser -> DeleteCommandParser : parse("1")
+FlashlingoParser -> DeleteCommandParser : parse("1")
activate DeleteCommandParser
create DeleteCommand
@@ -37,14 +37,14 @@ activate DeleteCommand
DeleteCommand --> DeleteCommandParser : d
deactivate DeleteCommand
-DeleteCommandParser --> AddressBookParser : d
+DeleteCommandParser --> FlashlingoParser : d
deactivate DeleteCommandParser
'Hidden arrow to position the destroy marker below the end of the activation bar.
-DeleteCommandParser -[hidden]-> AddressBookParser
+DeleteCommandParser -[hidden]-> FlashlingoParser
destroy DeleteCommandParser
-AddressBookParser --> LogicManager : d
-deactivate AddressBookParser
+FlashlingoParser --> LogicManager : d
+deactivate FlashlingoParser
LogicManager -> DeleteCommand : execute()
activate DeleteCommand
diff --git a/docs/diagrams/LoadCommandActivityDiagram.puml b/docs/diagrams/LoadCommandActivityDiagram.puml
new file mode 100644
index 00000000000..7da2c1b3069
--- /dev/null
+++ b/docs/diagrams/LoadCommandActivityDiagram.puml
@@ -0,0 +1,20 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 16
+skinparam ArrowFontSize 16
+
+start
+:Execute the LoadCommand;
+:Open the Excel file;
+if () then ([FileNotFoundException/SecurityException])
+ :Throw exception "open file fail";
+ stop
+else ()
+ :Create new XSSFWorkbook;
+ if () then ([IOException])
+ :Throw exception "read file fail";
+ else ()
+ : Read rows and update model ;
+ endif
+stop
+@enduml
diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml
index a57720890ee..8d0ad5c6f78 100644
--- a/docs/diagrams/LogicClassDiagram.puml
+++ b/docs/diagrams/LogicClassDiagram.puml
@@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR
package Logic as LogicPackage {
-Class AddressBookParser
+Class FlashlingoParser
Class XYZCommand
Class CommandResult
Class "{abstract}\nCommand" as Command
@@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF
HiddenOutside ..> Logic
LogicManager .right.|> Logic
-LogicManager -right->"1" AddressBookParser
-AddressBookParser ..> XYZCommand : creates >
+LogicManager -right->"1" FlashlingoParser
+FlashlingoParser ..> XYZCommand : creates >
XYZCommand -up-|> Command
LogicManager .left.> Command : executes >
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 0de5673070d..01ae55c5144 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -5,20 +5,18 @@ skinparam arrowColor MODEL_COLOR
skinparam classBackgroundColor MODEL_COLOR
Package Model as ModelPackage <>{
-Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook
+Class "<>\nReadOnlyFlashlingo" as ReadOnlyFlashlingo
Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs
Class "<>\nModel" as Model
-Class AddressBook
+Class Flashlingo
Class ModelManager
Class UserPrefs
-Class UniquePersonList
-Class Person
-Class Address
-Class Email
-Class Name
-Class Phone
-Class Tag
+Class UniqueFlashCardList
+Class Flashcard
+Class OriginalWord
+Class TranslatedWord
+Class ProficiencyLevel
Class I #FFFFFF
}
@@ -26,29 +24,28 @@ Class I #FFFFFF
Class HiddenOutside #FFFFFF
HiddenOutside ..> Model
-AddressBook .up.|> ReadOnlyAddressBook
+Flashlingo .up.|> ReadOnlyFlashlingo
ModelManager .up.|> Model
Model .right.> ReadOnlyUserPrefs
-Model .left.> ReadOnlyAddressBook
-ModelManager -left-> "1" AddressBook
+Model .left.> ReadOnlyFlashlingo
+ModelManager -left-> "1" Flashlingo
ModelManager -right-> "1" UserPrefs
UserPrefs .up.|> ReadOnlyUserPrefs
-AddressBook *--> "1" UniquePersonList
-UniquePersonList --> "~* all" Person
-Person *--> Name
-Person *--> Phone
-Person *--> Email
-Person *--> Address
-Person *--> "*" Tag
+Flashlingo *--> "1" UniqueFlashCardList
+UniqueFlashCardList --> "~* all" FlashCard
+FlashCard *--> OriginalWord
+FlashCard *--> TranslatedWord
+FlashCard *--> ProficiencyLevel
-Person -[hidden]up--> I
-UniquePersonList -[hidden]right-> I
-Name -[hidden]right-> Phone
-Phone -[hidden]right-> Address
-Address -[hidden]right-> Email
+FlashCard -[hidden]up--> I
+UniqueFlashCardList -[hidden]right-> I
-ModelManager --> "~* filtered" Person
+OriginalWord -[hidden]right-> OriginalWord
+TranslatedWord -[hidden]right-> TranslatedWord
+ProficiencyLevel -[hidden]right-> ProficiencyLevel
+
+ModelManager --> "~* filtered" FlashCard
@enduml
diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml
index 0c7424de6e0..4781b83250b 100644
--- a/docs/diagrams/ParserClasses.puml
+++ b/docs/diagrams/ParserClasses.puml
@@ -9,7 +9,7 @@ Class XYZCommand
package "Parser classes"{
Class "<>\nParser" as Parser
-Class AddressBookParser
+Class FlashlingoParser
Class XYZCommandParser
Class CliSyntax
Class ParserUtil
@@ -19,12 +19,12 @@ Class Prefix
}
Class HiddenOutside #FFFFFF
-HiddenOutside ..> AddressBookParser
+HiddenOutside ..> FlashlingoParser
-AddressBookParser .down.> XYZCommandParser: creates >
+FlashlingoParser .down.> XYZCommandParser: creates >
XYZCommandParser ..> XYZCommand : creates >
-AddressBookParser ..> Command : returns >
+FlashlingoParser ..> Command : returns >
XYZCommandParser .up.|> Parser
XYZCommandParser ..> ArgumentMultimap
XYZCommandParser ..> ArgumentTokenizer
diff --git a/docs/diagrams/ReadRowsActivityDiagram.puml b/docs/diagrams/ReadRowsActivityDiagram.puml
new file mode 100644
index 00000000000..6893c707aad
--- /dev/null
+++ b/docs/diagrams/ReadRowsActivityDiagram.puml
@@ -0,0 +1,27 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 14
+
+left header Activity: Read rows and update model
+
+start
+while () is ([Have filled row])
+ :Read row from the first sheet;
+ :Trim the words;
+ if () then ([Word is invalid])
+ :Throw exception "empty word";
+ stop
+ else ()
+ :Create FlashCard object;
+ :Add the object to temporary list;
+ endif
+ endwhile
+ :Add list of FlashCards to the model;
+ if () then ([Duplicate card])
+ :Throw exception "duplicate";
+ else ()
+ :Return CommandResult;
+ endif
+stop
+@enduml
diff --git a/docs/diagrams/StartSequenceDiagram.puml b/docs/diagrams/StartSequenceDiagram.puml
new file mode 100644
index 00000000000..988cef92e27
--- /dev/null
+++ b/docs/diagrams/StartSequenceDiagram.puml
@@ -0,0 +1,78 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":FlashlingoParser" as FlashlingoParser LOGIC_COLOR
+participant ":SessionManager" as SessionManager LOGIC_COLOR
+participant "s:StartCommand" as StartCommand LOGIC_COLOR
+participant "r:CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("start")
+activate LogicManager
+
+LogicManager -> FlashlingoParser : parseCommand("start")
+activate FlashlingoParser
+
+FlashlingoParser -> SessionManager : getInstance()
+activate SessionManager
+
+SessionManager -> SessionManager : SessionManager()
+activate SessionManager
+
+
+
+
+SessionManager --> FlashlingoParser : instance
+deactivate SessionManager
+
+FlashlingoParser -> SessionManager : isReviewSession()
+
+
+SessionManager --> FlashlingoParser : isReviewSession
+deactivate SessionManager
+
+
+create StartCommand
+FlashlingoParser -> StartCommand
+activate StartCommand
+
+StartCommand --> FlashlingoParser :
+deactivate StartCommand
+
+FlashlingoParser --> LogicManager : s
+deactivate FlashlingoParser
+
+LogicManager -> StartCommand : execute()
+activate StartCommand
+
+StartCommand -> SessionManager : setSession(true)
+activate SessionManager
+
+SessionManager --> StartCommand :
+deactivate SessionManager
+StartCommand -> Model : startSession()
+activate Model
+
+Model --> StartCommand
+deactivate Model
+
+create CommandResult
+StartCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> StartCommand
+deactivate CommandResult
+
+StartCommand --> LogicManager : r
+deactivate StartCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml
index a821e06458c..3cab402a51d 100644
--- a/docs/diagrams/StorageClassDiagram.puml
+++ b/docs/diagrams/StorageClassDiagram.puml
@@ -14,11 +14,11 @@ Class JsonUserPrefsStorage
Class "<>\nStorage" as Storage
Class StorageManager
-package "AddressBook Storage" #F4F6F6{
-Class "<>\nAddressBookStorage" as AddressBookStorage
-Class JsonAddressBookStorage
-Class JsonSerializableAddressBook
-Class JsonAdaptedPerson
+package "Flashlingo Storage" #F4F6F6{
+Class "<>\nFlashlingoStorage" as FlashlingoStorage
+Class JsonFlashlingoStorage
+Class JsonSerializableFlashlingo
+Class JsonAdaptedFlashCard
Class JsonAdaptedTag
}
@@ -29,15 +29,15 @@ HiddenOutside ..> Storage
StorageManager .up.|> Storage
StorageManager -up-> "1" UserPrefsStorage
-StorageManager -up-> "1" AddressBookStorage
+StorageManager -up-> "1" FlashlingoStorage
Storage -left-|> UserPrefsStorage
-Storage -right-|> AddressBookStorage
+Storage -right-|> FlashlingoStorage
JsonUserPrefsStorage .up.|> UserPrefsStorage
-JsonAddressBookStorage .up.|> AddressBookStorage
-JsonAddressBookStorage ..> JsonSerializableAddressBook
-JsonSerializableAddressBook --> "*" JsonAdaptedPerson
-JsonAdaptedPerson --> "*" JsonAdaptedTag
+JsonFlashlingoStorage .up.|> FlashlingoStorage
+JsonFlashlingoStorage ..> JsonSerializableFlashlingo
+JsonSerializableFlashlingo --> "*" JsonAdaptedFlashCard
+JsonAdaptedFlashCard --> "*" JsonAdaptedTag
@enduml
diff --git a/docs/diagrams/SwitchSequenceDiagram.puml b/docs/diagrams/SwitchSequenceDiagram.puml
new file mode 100644
index 00000000000..480fe7a9887
--- /dev/null
+++ b/docs/diagrams/SwitchSequenceDiagram.puml
@@ -0,0 +1,61 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":FlashlingoParser" as FlashlingoParser LOGIC_COLOR
+participant "c:SwitchCommand" as SwitchCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant "m:Model" as Model MODEL_COLOR
+end box
+
+box Storage STORAGE_COLOR_T1
+participant ":Storage" as Storage STORAGE_COLOR
+end box
+
+[-> LogicManager : execute("switch")
+activate LogicManager
+
+LogicManager -> FlashlingoParser : parseCommand("switch")
+activate FlashlingoParser
+
+create SwitchCommand
+FlashlingoParser -> SwitchCommand
+activate SwitchCommand
+
+SwitchCommand --> FlashlingoParser
+deactivate SwitchCommand
+
+FlashlingoParser --> LogicManager : c
+deactivate FlashlingoParser
+
+LogicManager -> SwitchCommand : execute(m)
+activate SwitchCommand
+
+SwitchCommand -> Model : switchTheme()
+activate Model
+
+Model --> SwitchCommand
+deactivate Model
+
+SwitchCommand --> LogicManager : commandResult
+deactivate SwitchCommand
+
+LogicManager -> Model : getUserPrefs()
+activate Model
+
+Model --> LogicManager
+deactivate Model
+
+LogicManager -> Storage : saveUserPrefs(userPrefs)
+activate Storage
+
+Storage --> LogicManager
+deactivate Storage
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml
index 95473d5aa19..303b7046956 100644
--- a/docs/diagrams/UiClassDiagram.puml
+++ b/docs/diagrams/UiClassDiagram.puml
@@ -11,8 +11,9 @@ Class UiManager
Class MainWindow
Class HelpWindow
Class ResultDisplay
-Class PersonListPanel
-Class PersonCard
+Class FlashcardListPanel
+Class FlashcardBox
+Class FlashcardBoxNoButton
Class StatusBarFooter
Class CommandBox
}
@@ -32,26 +33,33 @@ UiManager .left.|> Ui
UiManager -down-> "1" MainWindow
MainWindow *-down-> "1" CommandBox
MainWindow *-down-> "1" ResultDisplay
-MainWindow *-down-> "1" PersonListPanel
+MainWindow *-down-> "1" FlashcardListPanel
MainWindow *-down-> "1" StatusBarFooter
MainWindow --> "0..1" HelpWindow
-PersonListPanel -down-> "*" PersonCard
+FlashcardListPanel -down-> "*" FlashcardBox
+FlashcardListPanel -down-> "*" FlashcardBoxNoButton
MainWindow -left-|> UiPart
ResultDisplay --|> UiPart
CommandBox --|> UiPart
-PersonListPanel --|> UiPart
-PersonCard --|> UiPart
+FlashcardListPanel --|> UiPart
+FlashcardBox --|> UiPart
+FlashcardBoxNoButton --|> UiPart
StatusBarFooter --|> UiPart
HelpWindow --|> UiPart
+MainWindow --|> UiPart
-PersonCard ..> Model
-UiManager -right-> Logic
+FlashcardBox ..> Model
+FlashcardBoxNoButton ..> Model
+
+FlashcardBox --> "1" MainWindow
+FlashcardBoxNoButton --> "1" MainWindow
+UiManager -right-> MainWindow
MainWindow -left-> Logic
-PersonListPanel -[hidden]left- HelpWindow
+FlashcardListPanel -[hidden]left- HelpWindow
HelpWindow -[hidden]left- CommandBox
CommandBox -[hidden]left- ResultDisplay
ResultDisplay -[hidden]left- StatusBarFooter
diff --git a/docs/diagrams/UiNoButtonSequenceDiagram.puml b/docs/diagrams/UiNoButtonSequenceDiagram.puml
new file mode 100644
index 00000000000..df692d9b527
--- /dev/null
+++ b/docs/diagrams/UiNoButtonSequenceDiagram.puml
@@ -0,0 +1,50 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box LOGIC_COLOR_T1
+participant ":FlashcardBox" as FlashcardBox LOGIC_COLOR
+participant ":MainWindow" as MainWindow LOGIC_COLOR
+participant "logic:Logic" as Logic LOGIC_COLOR
+participant ":FlashlingoParser" as FlashlingoParser LOGIC_COLOR
+participant "command:YesCommand" as YesCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> FlashcardBox : success()
+activate FlashcardBox
+
+
+FlashcardBox -> MainWindow : executeCommand("no")
+activate MainWindow
+
+MainWindow -> Logic : executeCommand("no")
+activate Logic
+
+Logic -> FlashlingoParser : parseCommand("no")
+activate FlashlingoParser
+return command
+
+Logic -> YesCommand : execute(<>)
+activate YesCommand
+YesCommand -> Model : rememberWord(false)
+activate Model
+
+return
+return commandResult
+return commandResult
+
+MainWindow -> CommandResult : getFeedbackToUser()
+activate CommandResult
+return
+
+
+MainWindow --> FlashcardBox
+deactivate MainWindow
+<--FlashcardBox
+deactivate FlashcardBox
+@enduml
diff --git a/docs/diagrams/UiYesButtonSequenceDiagram.puml b/docs/diagrams/UiYesButtonSequenceDiagram.puml
new file mode 100644
index 00000000000..c102e593a95
--- /dev/null
+++ b/docs/diagrams/UiYesButtonSequenceDiagram.puml
@@ -0,0 +1,50 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box LOGIC_COLOR_T1
+participant ":FlashcardBox" as FlashcardBox LOGIC_COLOR
+participant ":MainWindow" as MainWindow LOGIC_COLOR
+participant "logic:Logic" as Logic LOGIC_COLOR
+participant ":FlashlingoParser" as FlashlingoParser LOGIC_COLOR
+participant "command:YesCommand" as YesCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+
+[-> FlashcardBox : success()
+activate FlashcardBox
+
+
+FlashcardBox -> MainWindow : executeCommand("yes")
+activate MainWindow
+
+MainWindow -> Logic : executeCommand("yes")
+activate Logic
+
+Logic -> FlashlingoParser : parseCommand("yes")
+activate FlashlingoParser
+return command
+
+Logic -> YesCommand : execute(<>)
+activate YesCommand
+YesCommand -> Model : rememberWord(true)
+activate Model
+
+return
+return commandResult
+return commandResult
+
+MainWindow -> CommandResult : getFeedbackToUser()
+activate CommandResult
+return
+
+
+MainWindow --> FlashcardBox
+deactivate MainWindow
+<--FlashcardBox
+deactivate FlashcardBox
+@enduml
diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml
deleted file mode 100644
index 43a45903ac9..00000000000
--- a/docs/diagrams/UndoRedoState0.puml
+++ /dev/null
@@ -1,21 +0,0 @@
-@startuml
-!include style.puml
-skinparam ClassFontColor #000000
-skinparam ClassBorderColor #000000
-skinparam ClassBackgroundColor #FFFFAA
-
-title Initial state
-
-package States {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab2:AddressBook"
-}
-State1 -[hidden]right-> State2
-State2 -[hidden]right-> State3
-hide State2
-hide State3
-
-class Pointer as "Current State" #FFFFFF
-Pointer -up-> State1
-@end
diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml
deleted file mode 100644
index 5a41e9e1651..00000000000
--- a/docs/diagrams/UndoRedoState1.puml
+++ /dev/null
@@ -1,23 +0,0 @@
-@startuml
-!include style.puml
-skinparam ClassFontColor #000000
-skinparam ClassBorderColor #000000
-skinparam ClassBackgroundColor #FFFFAA
-
-title After command "delete 5"
-
-package States <> {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab2:AddressBook"
-}
-
-State1 -[hidden]right-> State2
-State2 -[hidden]right-> State3
-
-hide State3
-
-class Pointer as "Current State" #FFFFFF
-
-Pointer -up-> State2
-@end
diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml
deleted file mode 100644
index ad32fce1b0b..00000000000
--- a/docs/diagrams/UndoRedoState2.puml
+++ /dev/null
@@ -1,21 +0,0 @@
-@startuml
-!include style.puml
-skinparam ClassFontColor #000000
-skinparam ClassBorderColor #000000
-skinparam ClassBackgroundColor #FFFFAA
-
-title After command "add n/David"
-
-package States <> {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab2:AddressBook"
-}
-
-State1 -[hidden]right-> State2
-State2 -[hidden]right-> State3
-
-class Pointer as "Current State" #FFFFFF
-
-Pointer -up-> State3
-@end
diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml
deleted file mode 100644
index 9187a690036..00000000000
--- a/docs/diagrams/UndoRedoState3.puml
+++ /dev/null
@@ -1,21 +0,0 @@
-@startuml
-!include style.puml
-skinparam ClassFontColor #000000
-skinparam ClassBorderColor #000000
-skinparam ClassBackgroundColor #FFFFAA
-
-title After command "undo"
-
-package States <> {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab2:AddressBook"
-}
-
-State1 -[hidden]right-> State2
-State2 -[hidden]right-> State3
-
-class Pointer as "Current State" #FFFFFF
-
-Pointer -up-> State2
-@end
diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml
deleted file mode 100644
index 2bc631ffcd0..00000000000
--- a/docs/diagrams/UndoRedoState4.puml
+++ /dev/null
@@ -1,21 +0,0 @@
-@startuml
-!include style.puml
-skinparam ClassFontColor #000000
-skinparam ClassBorderColor #000000
-skinparam ClassBackgroundColor #FFFFAA
-
-title After command "list"
-
-package States <> {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab2:AddressBook"
-}
-
-State1 -[hidden]right-> State2
-State2 -[hidden]right-> State3
-
-class Pointer as "Current State" #FFFFFF
-
-Pointer -up-> State2
-@end
diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml
deleted file mode 100644
index e77b04104aa..00000000000
--- a/docs/diagrams/UndoRedoState5.puml
+++ /dev/null
@@ -1,22 +0,0 @@
-@startuml
-!include style.puml
-skinparam ClassFontColor #000000
-skinparam ClassBorderColor #000000
-skinparam ClassBackgroundColor #FFFFAA
-
-title After command "clear"
-
-package States <> {
- class State1 as "ab0:AddressBook"
- class State2 as "ab1:AddressBook"
- class State3 as "ab3:AddressBook"
-}
-
-State1 -[hidden]right-> State2
-State2 -[hidden]right-> State3
-
-class Pointer as "Current State" #FFFFFF
-
-Pointer -up-> State3
-note right on link: State ab2 deleted.
-@end
diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml
deleted file mode 100644
index 87ff3e9237e..00000000000
--- a/docs/diagrams/UndoSequenceDiagram.puml
+++ /dev/null
@@ -1,54 +0,0 @@
-@startuml
-!include style.puml
-skinparam ArrowFontStyle plain
-
-box Logic LOGIC_COLOR_T1
-participant ":LogicManager" as LogicManager LOGIC_COLOR
-participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
-participant "u:UndoCommand" as UndoCommand LOGIC_COLOR
-end box
-
-box Model MODEL_COLOR_T1
-participant ":Model" as Model MODEL_COLOR
-participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR
-end box
-[-> LogicManager : execute(undo)
-activate LogicManager
-
-LogicManager -> AddressBookParser : parseCommand(undo)
-activate AddressBookParser
-
-create UndoCommand
-AddressBookParser -> UndoCommand
-activate UndoCommand
-
-UndoCommand --> AddressBookParser
-deactivate UndoCommand
-
-AddressBookParser --> LogicManager : u
-deactivate AddressBookParser
-
-LogicManager -> UndoCommand : execute()
-activate UndoCommand
-
-UndoCommand -> Model : undoAddressBook()
-activate Model
-
-Model -> VersionedAddressBook : undo()
-activate VersionedAddressBook
-
-VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook)
-VersionedAddressBook --> Model :
-deactivate VersionedAddressBook
-
-Model --> UndoCommand
-deactivate Model
-
-UndoCommand --> LogicManager : result
-deactivate UndoCommand
-UndoCommand -[hidden]-> LogicManager : result
-destroy UndoCommand
-
-[<--LogicManager
-deactivate LogicManager
-@enduml
diff --git a/docs/diagrams/YesCommandActivityDiagram.puml b/docs/diagrams/YesCommandActivityDiagram.puml
new file mode 100644
index 00000000000..412659b6a8e
--- /dev/null
+++ b/docs/diagrams/YesCommandActivityDiagram.puml
@@ -0,0 +1,20 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+
+start
+:User executes YesCommand;
+:Call execute(Model);
+:Call nextReviewWord();
+:Call updateLevel(true);
+:Call recallFlashCard();
+if () then ([hasNextRound()])
+ :Call nextReviewWord();
+else ([else])
+ :Set Session to false;
+ :Update Filtered FlashCard List;
+ :Generate CommandResult;
+endif
+stop
+@enduml
diff --git a/docs/diagrams/YesSequenceDiagram.puml b/docs/diagrams/YesSequenceDiagram.puml
new file mode 100644
index 00000000000..f014d97de7f
--- /dev/null
+++ b/docs/diagrams/YesSequenceDiagram.puml
@@ -0,0 +1,61 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":FlashlingoParser" as FlashlingoParser LOGIC_COLOR
+participant "s:YesCommand" as YesCommand LOGIC_COLOR
+participant ":SessionManager" as SessionManager LOGIC_COLOR
+participant "s:CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+participant ":FlashCard" as FlashCard MODEL_COLOR
+end box
+
+[-> LogicManager : execute("yes")
+activate LogicManager
+
+LogicManager -> FlashlingoParser : parseCommand("yes")
+activate FlashlingoParser
+
+create YesCommand
+FlashlingoParser -> YesCommand : Create instance
+activate YesCommand
+
+
+YesCommand -> Model: nextReviewWord()
+activate Model
+YesCommand -> FlashCard: updateLevel(true)
+activate FlashCard
+YesCommand -> FlashCard: recallFlashCard()
+deactivate FlashCard
+YesCommand -> Model: hasNextRound()
+
+alt No more words to review
+ YesCommand -> SessionManager: setSession(false)
+ activate SessionManager
+ SessionManager -> Model: updateFilteredFlashCardList()
+ deactivate SessionManager
+ create CommandResult
+ YesCommand -> CommandResult : CommandResult()
+ activate CommandResult
+ CommandResult --> LogicManager : return CommandResult
+ deactivate CommandResult
+else More words to review
+ YesCommand -> Model: nextReviewWord()
+ deactivate Model
+ create CommandResult
+ YesCommand -> CommandResult : CommandResult()
+ activate CommandResult
+ CommandResult --> LogicManager : return CommandResult
+ deactivate CommandResult
+end
+
+deactivate FlashlingoParser
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml
index 42bf46d3ce8..6acbe9c6e55 100644
--- a/docs/diagrams/tracing/LogicSequenceDiagram.puml
+++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml
@@ -3,7 +3,7 @@
skinparam ArrowFontStyle plain
Participant ":LogicManager" as logic LOGIC_COLOR
-Participant ":AddressBookParser" as abp LOGIC_COLOR
+Participant ":FlashlingoParser" as abp LOGIC_COLOR
Participant ":EditCommandParser" as ecp LOGIC_COLOR
Participant "command:EditCommand" as ec LOGIC_COLOR
@@ -14,7 +14,7 @@ create ecp
abp -> ecp
abp -> ecp ++: parse(arguments)
create ec
-ecp -> ec ++: index, editPersonDescriptor
+ecp -> ec ++: index, editFlashCardDescriptor
ec --> ecp --
ecp --> abp --: command
abp --> logic --: command
diff --git a/docs/images/AddCommand.png b/docs/images/AddCommand.png
new file mode 100644
index 00000000000..aff0080b362
Binary files /dev/null and b/docs/images/AddCommand.png differ
diff --git a/docs/images/AddTagsSequenceDiagram.png b/docs/images/AddTagsSequenceDiagram.png
new file mode 100644
index 00000000000..5d98a6e6161
Binary files /dev/null and b/docs/images/AddTagsSequenceDiagram.png differ
diff --git a/docs/images/AfterDelete.png b/docs/images/AfterDelete.png
new file mode 100644
index 00000000000..09ae7b00c0e
Binary files /dev/null and b/docs/images/AfterDelete.png differ
diff --git a/docs/images/AfterEdit.png b/docs/images/AfterEdit.png
new file mode 100644
index 00000000000..e9543cc5268
Binary files /dev/null and b/docs/images/AfterEdit.png differ
diff --git a/docs/images/AfterFind.png b/docs/images/AfterFind.png
new file mode 100644
index 00000000000..505482ee80d
Binary files /dev/null and b/docs/images/AfterFind.png differ
diff --git a/docs/images/AfterReveal.png b/docs/images/AfterReveal.png
new file mode 100644
index 00000000000..4562c445e5f
Binary files /dev/null and b/docs/images/AfterReveal.png differ
diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png
index 37ad06a2803..b77ef11ff21 100644
Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ
diff --git a/docs/images/BeforeDelete.png b/docs/images/BeforeDelete.png
new file mode 100644
index 00000000000..972aedb8dd8
Binary files /dev/null and b/docs/images/BeforeDelete.png differ
diff --git a/docs/images/BeforeEdit.png b/docs/images/BeforeEdit.png
new file mode 100644
index 00000000000..2d38e65f58c
Binary files /dev/null and b/docs/images/BeforeEdit.png differ
diff --git a/docs/images/BeforeFind.png b/docs/images/BeforeFind.png
new file mode 100644
index 00000000000..b4e65d80ddf
Binary files /dev/null and b/docs/images/BeforeFind.png differ
diff --git a/docs/images/BeforeReveal.png b/docs/images/BeforeReveal.png
new file mode 100644
index 00000000000..2cd832aa24c
Binary files /dev/null and b/docs/images/BeforeReveal.png differ
diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png
deleted file mode 100644
index 02a42e35e76..00000000000
Binary files a/docs/images/BetterModelClassDiagram.png and /dev/null differ
diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png
deleted file mode 100644
index 5b464126b35..00000000000
Binary files a/docs/images/CommitActivityDiagram.png and /dev/null differ
diff --git a/docs/images/DarkTheme.png b/docs/images/DarkTheme.png
new file mode 100644
index 00000000000..736f5491e77
Binary files /dev/null and b/docs/images/DarkTheme.png differ
diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png
index e186f7ba096..abb59e0a6aa 100644
Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ
diff --git a/docs/images/FindMessage.png b/docs/images/FindMessage.png
new file mode 100644
index 00000000000..d094be79eb5
Binary files /dev/null and b/docs/images/FindMessage.png differ
diff --git a/docs/images/LanguageTag.png b/docs/images/LanguageTag.png
new file mode 100644
index 00000000000..405e43ca96b
Binary files /dev/null and b/docs/images/LanguageTag.png differ
diff --git a/docs/images/LightTheme.png b/docs/images/LightTheme.png
new file mode 100644
index 00000000000..6647a3e8e8f
Binary files /dev/null and b/docs/images/LightTheme.png differ
diff --git a/docs/images/ListCommand.png b/docs/images/ListCommand.png
new file mode 100644
index 00000000000..ab451d48010
Binary files /dev/null and b/docs/images/ListCommand.png differ
diff --git a/docs/images/LoadCommandActivityDiagram.png b/docs/images/LoadCommandActivityDiagram.png
new file mode 100644
index 00000000000..c1933e4253b
Binary files /dev/null and b/docs/images/LoadCommandActivityDiagram.png differ
diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png
index e3b784310fe..860a7f51c17 100644
Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ
diff --git a/docs/images/LogicStorageDIP.png b/docs/images/LogicStorageDIP.png
deleted file mode 100644
index 871157f5a9c..00000000000
Binary files a/docs/images/LogicStorageDIP.png and /dev/null differ
diff --git a/docs/images/MainUi.png b/docs/images/MainUi.png
new file mode 100644
index 00000000000..76bd9b65fbf
Binary files /dev/null and b/docs/images/MainUi.png differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index a19fb1b4ac8..2cbf29e8e0b 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/No.png b/docs/images/No.png
new file mode 100644
index 00000000000..1e3f8b451a1
Binary files /dev/null and b/docs/images/No.png differ
diff --git a/docs/images/No2.png b/docs/images/No2.png
new file mode 100644
index 00000000000..46ad5ab3673
Binary files /dev/null and b/docs/images/No2.png differ
diff --git a/docs/images/OutputWithSmallerFont.png b/docs/images/OutputWithSmallerFont.png
new file mode 100644
index 00000000000..2d2445eb617
Binary files /dev/null and b/docs/images/OutputWithSmallerFont.png differ
diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png
index edfd1ff7897..d223fb0fd68 100644
Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ
diff --git a/docs/images/ReadRowsActivityDiagram.png b/docs/images/ReadRowsActivityDiagram.png
new file mode 100644
index 00000000000..2a1b9fcb551
Binary files /dev/null and b/docs/images/ReadRowsActivityDiagram.png differ
diff --git a/docs/images/Reveal.png b/docs/images/Reveal.png
new file mode 100644
index 00000000000..af75d7fd385
Binary files /dev/null and b/docs/images/Reveal.png differ
diff --git a/docs/images/ReviewMessage.png b/docs/images/ReviewMessage.png
new file mode 100644
index 00000000000..3aa605adcd4
Binary files /dev/null and b/docs/images/ReviewMessage.png differ
diff --git a/docs/images/ReviewOver.png b/docs/images/ReviewOver.png
new file mode 100644
index 00000000000..57e35712ff3
Binary files /dev/null and b/docs/images/ReviewOver.png differ
diff --git a/docs/images/ReviewSuccess.png b/docs/images/ReviewSuccess.png
new file mode 100644
index 00000000000..c354a9d3106
Binary files /dev/null and b/docs/images/ReviewSuccess.png differ
diff --git a/docs/images/SeEduLogo.png b/docs/images/SeEduLogo.png
deleted file mode 100644
index 31ad50b6f88..00000000000
Binary files a/docs/images/SeEduLogo.png and /dev/null differ
diff --git a/docs/images/SessionManagerClass.png b/docs/images/SessionManagerClass.png
new file mode 100644
index 00000000000..d53a24a0620
Binary files /dev/null and b/docs/images/SessionManagerClass.png differ
diff --git a/docs/images/StartSequenceDiagram.png b/docs/images/StartSequenceDiagram.png
new file mode 100644
index 00000000000..0eab7ee0b8e
Binary files /dev/null and b/docs/images/StartSequenceDiagram.png differ
diff --git a/docs/images/Stats.png b/docs/images/Stats.png
new file mode 100644
index 00000000000..a7d759b4902
Binary files /dev/null and b/docs/images/Stats.png differ
diff --git a/docs/images/StatsChart.png b/docs/images/StatsChart.png
new file mode 100644
index 00000000000..aa2e0897efc
Binary files /dev/null and b/docs/images/StatsChart.png differ
diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png
index 18fa4d0d51f..e6833b6917e 100644
Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ
diff --git a/docs/images/SwitchSequenceDiagram.png b/docs/images/SwitchSequenceDiagram.png
new file mode 100644
index 00000000000..f9923d4223a
Binary files /dev/null and b/docs/images/SwitchSequenceDiagram.png differ
diff --git a/docs/images/Terminal.png b/docs/images/Terminal.png
new file mode 100644
index 00000000000..9941dd481fa
Binary files /dev/null and b/docs/images/Terminal.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..76bd9b65fbf 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png
index 11f06d68671..d2c8e6652dd 100644
Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ
diff --git a/docs/images/UiNoButtonSequenceDiagram.png b/docs/images/UiNoButtonSequenceDiagram.png
new file mode 100644
index 00000000000..c940fed4518
Binary files /dev/null and b/docs/images/UiNoButtonSequenceDiagram.png differ
diff --git a/docs/images/UiYesButtonSequenceDiagram.png b/docs/images/UiYesButtonSequenceDiagram.png
new file mode 100644
index 00000000000..f88f6cc2f71
Binary files /dev/null and b/docs/images/UiYesButtonSequenceDiagram.png differ
diff --git a/docs/images/UndoRedoState0.png b/docs/images/UndoRedoState0.png
deleted file mode 100644
index c5f91b58533..00000000000
Binary files a/docs/images/UndoRedoState0.png and /dev/null differ
diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png
deleted file mode 100644
index 2d3ad09c047..00000000000
Binary files a/docs/images/UndoRedoState1.png and /dev/null differ
diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png
deleted file mode 100644
index 20853694e03..00000000000
Binary files a/docs/images/UndoRedoState2.png and /dev/null differ
diff --git a/docs/images/UndoRedoState3.png b/docs/images/UndoRedoState3.png
deleted file mode 100644
index 1a9551b31be..00000000000
Binary files a/docs/images/UndoRedoState3.png and /dev/null differ
diff --git a/docs/images/UndoRedoState4.png b/docs/images/UndoRedoState4.png
deleted file mode 100644
index 46dfae78c94..00000000000
Binary files a/docs/images/UndoRedoState4.png and /dev/null differ
diff --git a/docs/images/UndoRedoState5.png b/docs/images/UndoRedoState5.png
deleted file mode 100644
index f45889b5fdf..00000000000
Binary files a/docs/images/UndoRedoState5.png and /dev/null differ
diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png
deleted file mode 100644
index c7a7e637266..00000000000
Binary files a/docs/images/UndoSequenceDiagram.png and /dev/null differ
diff --git a/docs/images/Yes.png b/docs/images/Yes.png
new file mode 100644
index 00000000000..6dd4c1ef9d5
Binary files /dev/null and b/docs/images/Yes.png differ
diff --git a/docs/images/Yes2.png b/docs/images/Yes2.png
new file mode 100644
index 00000000000..fd8d7c80865
Binary files /dev/null and b/docs/images/Yes2.png differ
diff --git a/docs/images/YesCommandActivityDiagram.png b/docs/images/YesCommandActivityDiagram.png
new file mode 100644
index 00000000000..b6956c25875
Binary files /dev/null and b/docs/images/YesCommandActivityDiagram.png differ
diff --git a/docs/images/YesSequenceDiagram.png b/docs/images/YesSequenceDiagram.png
new file mode 100644
index 00000000000..2350f56918a
Binary files /dev/null and b/docs/images/YesSequenceDiagram.png differ
diff --git a/docs/images/a1waysd.png b/docs/images/a1waysd.png
new file mode 100644
index 00000000000..6b7a8a282d8
Binary files /dev/null and b/docs/images/a1waysd.png differ
diff --git a/docs/images/add-remark/$Remark.png b/docs/images/add-remark/$Remark.png
deleted file mode 100644
index 959c634406d..00000000000
Binary files a/docs/images/add-remark/$Remark.png and /dev/null differ
diff --git a/docs/images/add-remark/ContextMenu.png b/docs/images/add-remark/ContextMenu.png
deleted file mode 100644
index 77536724e45..00000000000
Binary files a/docs/images/add-remark/ContextMenu.png and /dev/null differ
diff --git a/docs/images/add-remark/CreateTest.png b/docs/images/add-remark/CreateTest.png
deleted file mode 100644
index 6b7d6dcafec..00000000000
Binary files a/docs/images/add-remark/CreateTest.png and /dev/null differ
diff --git a/docs/images/add-remark/GradleRun.png b/docs/images/add-remark/GradleRun.png
deleted file mode 100644
index 281cc45f098..00000000000
Binary files a/docs/images/add-remark/GradleRun.png and /dev/null differ
diff --git a/docs/images/add-remark/RemarkBound.png b/docs/images/add-remark/RemarkBound.png
deleted file mode 100644
index d335382b286..00000000000
Binary files a/docs/images/add-remark/RemarkBound.png and /dev/null differ
diff --git a/docs/images/add-remark/RemarkCommandClass.png b/docs/images/add-remark/RemarkCommandClass.png
deleted file mode 100644
index 5687a3e9585..00000000000
Binary files a/docs/images/add-remark/RemarkCommandClass.png and /dev/null differ
diff --git a/docs/images/add-remark/RemarkCommandParserClass.png b/docs/images/add-remark/RemarkCommandParserClass.png
deleted file mode 100644
index d5ad9c8a02f..00000000000
Binary files a/docs/images/add-remark/RemarkCommandParserClass.png and /dev/null differ
diff --git a/docs/images/add-remark/RemarkComplete.png b/docs/images/add-remark/RemarkComplete.png
deleted file mode 100644
index 124ced2c752..00000000000
Binary files a/docs/images/add-remark/RemarkComplete.png and /dev/null differ
diff --git a/docs/images/add-remark/RemarkFailureOutput.png b/docs/images/add-remark/RemarkFailureOutput.png
deleted file mode 100644
index 351257ea332..00000000000
Binary files a/docs/images/add-remark/RemarkFailureOutput.png and /dev/null differ
diff --git a/docs/images/add-remark/RemarkHello.png b/docs/images/add-remark/RemarkHello.png
deleted file mode 100644
index aad48d02f8f..00000000000
Binary files a/docs/images/add-remark/RemarkHello.png and /dev/null differ
diff --git a/docs/images/add-remark/RemarkNotImplemented.png b/docs/images/add-remark/RemarkNotImplemented.png
deleted file mode 100644
index 1d187f39403..00000000000
Binary files a/docs/images/add-remark/RemarkNotImplemented.png and /dev/null differ
diff --git a/docs/images/d-limiter.png b/docs/images/d-limiter.png
new file mode 100644
index 00000000000..d421ccded06
Binary files /dev/null and b/docs/images/d-limiter.png differ
diff --git a/docs/images/d_limiter.png b/docs/images/d_limiter.png
new file mode 100644
index 00000000000..d421ccded06
Binary files /dev/null and b/docs/images/d_limiter.png differ
diff --git a/docs/images/findAlexDavidResult.png b/docs/images/findAlexDavidResult.png
deleted file mode 100644
index 235da1c273e..00000000000
Binary files a/docs/images/findAlexDavidResult.png and /dev/null differ
diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png
deleted file mode 100644
index b1f70470137..00000000000
Binary files a/docs/images/helpMessage.png and /dev/null differ
diff --git a/docs/images/itsnattan.png b/docs/images/itsnattan.png
new file mode 100644
index 00000000000..5f296e862b3
Binary files /dev/null and b/docs/images/itsnattan.png differ
diff --git a/docs/images/remove/$address.png b/docs/images/remove/$address.png
deleted file mode 100644
index cf0434e0e83..00000000000
Binary files a/docs/images/remove/$address.png and /dev/null differ
diff --git a/docs/images/remove/SafeDeleteConflicts.png b/docs/images/remove/SafeDeleteConflicts.png
deleted file mode 100644
index 8f0abeffd4d..00000000000
Binary files a/docs/images/remove/SafeDeleteConflicts.png and /dev/null differ
diff --git a/docs/images/remove/UnsafeDelete.png b/docs/images/remove/UnsafeDelete.png
deleted file mode 100644
index 9e376d02a0c..00000000000
Binary files a/docs/images/remove/UnsafeDelete.png and /dev/null differ
diff --git a/docs/images/remove/UnsafeDeleteOnField.png b/docs/images/remove/UnsafeDeleteOnField.png
deleted file mode 100644
index 44d5bb0a442..00000000000
Binary files a/docs/images/remove/UnsafeDeleteOnField.png and /dev/null differ
diff --git a/docs/images/request_access.png b/docs/images/request_access.png
deleted file mode 100644
index 12e8a81bd28..00000000000
Binary files a/docs/images/request_access.png and /dev/null differ
diff --git a/docs/images/johndoe.png b/docs/images/song-mengfei.png
similarity index 100%
rename from docs/images/johndoe.png
rename to docs/images/song-mengfei.png
diff --git a/docs/images/wangcheng0116.png b/docs/images/wangcheng0116.png
new file mode 100644
index 00000000000..a8b8fe86e0c
Binary files /dev/null and b/docs/images/wangcheng0116.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..61a0e8c464d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,19 +1,19 @@
---
layout: page
-title: AddressBook Level-3
+title: Flashlingo
---
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
-[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3)
+[![CI Status](https://github.com/AY2324S1-CS2103T-T11-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2324S1-CS2103T-T11-4/tp/actions)
+[![codecov](https://codecov.io/gh/AY2324S1-CS2103T-T11-4/tp/branch/master/graph/badge.svg)](https://codecov.io/gh/AY2324S1-CS2103T-T11-4/tp)
-![Ui](images/Ui.png)
+![Ui](images/MainUi.png)
-**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
+**Flashlingo is a desktop application for learning words by flash cards.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
-* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
-* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
+* If you are interested in using Flashlingo, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
+* If you are interested about developing Flashlingo, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
**Acknowledgements**
-* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5)
+* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5), [Apache POI](https://poi.apache.org/)
diff --git a/docs/team/a1waysd.md b/docs/team/a1waysd.md
new file mode 100644
index 00000000000..0f8978b5f7a
--- /dev/null
+++ b/docs/team/a1waysd.md
@@ -0,0 +1,48 @@
+---
+layout: page
+title: Yu Jiali's Project Portfolio Page
+---
+
+### Project: Flashlingo
+
+FlashLingo is a desktop application to facilitate the learning of new languages through flashcards. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added a `load` command that allows users to load their words and translations from Excel files to Flashlingo.
+ * What it does: Reads data from file, creates and stores new `FlashCard` objects.
+ * Justification: This feature significantly improves the product to make it more functional to our target users. It allows them to load customized flash cards conveniently and start to learn them immediately, which is more efficient than adding them one by one.
+ * Highlights: As the data is stored in a complex structure, any exceptions or invalid data need to be handled thoroughly. Moreover, duplicated data should be avoided.
+ * Credits: Third-party library _apache-poi-ooxml_ is used to read data from Excel.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=a1waysd&breakdown=true)
+
+* **Enhancements to existing features**:
+ * Improved CSS styling of GUI, such as font and layout
+ * Introduced dark theme, enabled theme preference saving and loading
+ * Refactored `storage`, `model` and `logic` components to fit with Flashlingo and flash cards
+ * Added more defensive coding in `storage` to prevent data and app corruption
+
+* **Documentation**:
+ * User Guide:
+ * Drafted UI, `list` and `stop` commands, refactored title and layout
+ * Added documentation for `switch`, `load` and `help` commands
+ * Wrote FAQ section in detail
+ * Updated and cropped all images, improved overall word usage and format
+ * Developer Guide:
+ * Drafted project scope, NFRs and manual testing
+ * Adapted the storage design from AB-3 to Flashlingo
+ * Added documentation for `load` and `switch` commands implementation
+ * Wrote planned enhancement section and updated NFRs
+
+* **Contributions to team-based tasks**:
+ * Milestone v1.1, v1.2, v1.2b and v1.3b creation, issues assignment
+ * In charge of [project docs](https://docs.google.com/document/d/1I7AeOjEK5tkLxLRfuDXVB0-02TU7aJH4cUiQuE5mahE/edit#heading=h.mgdo413wsg9o), including v1.2, v1.3 features demo and postmortem
+ * Task assignment, in-team coordination, task presenter including PE-D bug fix report and tester feedback
+ * Effective [Refactor of code](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/75) from AB-3 to Flashlingo
+ * Documented FAQ in User Guide, project scope and NFRs in Developer Guide
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): [#185](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/185), [#232](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/232), [#311](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/311)
+ * Contributed to [forum discussions](https://github.com/nus-cs2103-AY2324S1/forum/issues/145)
+ * Reported bugs in other team's products: [PE-D](https://github.com/A1WAYSD/ped/issues)
+
diff --git a/docs/team/d-limiter.md b/docs/team/d-limiter.md
new file mode 100644
index 00000000000..76e43ef013f
--- /dev/null
+++ b/docs/team/d-limiter.md
@@ -0,0 +1,37 @@
+---
+layout: page
+title: Taanish Bhardwaj's Project Portfolio Page
+---
+
+### Project: Flashlingo
+
+FlashLingo is a desktop application to facilitate the learning of new languages through flashcards. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added `stats` command for comprehensive learning analysis
+ * What it does: This command provides users with detailed learning statistics. It showcases the total count of flashcards, the number of flashcards successfully remembered, and the user's overall success rate.
+ * Justification: This feature is specifically designed to motivate novice language learners, our target user group, by visually representing their learning progress. The success rate metric serves as a continuous source of inspiration, encouraging users to improve their language skills.
+ * Highlights: The development of this feature required an in-depth understanding of Flashlingo's model and its flashcard list management. Key challenges involved accessing and processing the entire flashcard list to accurately calculate the success rate. This necessitated intricate integration with the existing system architecture, ensuring that the calculations are accurate and reflect real-time learning progress. The implementation not only provided users with valuable insights but also demonstrated a sophisticated application of data analysis within the Flashlingo environment.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=D-Limiter&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22&tabOpen=false)
+* **Enhancements to existing features**:
+ * Enhanced the functionality for storing words in flashcards by creating a unified `Word` class. [#104](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/104)
+ * Implemented language parameters for each word in their respective flashcards to support multilingual functionality. [#103](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/103)
+ * Conducted extensive and thorough unit testing for each command to ensure of their accuracy and correctness. [#220](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/220)
+* **Documentation**:
+ * User Guide:
+ * Drafted the documentation for `add` and `delete` commands
+ * Conducted a thorough review and correction of grammatical errors across various sections of the User Guide to enhance readability and clarity.
+ * Authored the comprehensive documentation for `list`, `review`, `stats`, and `exit` commands. [#323](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/323)
+ * Developer Guide:
+ * Developed and integrated comprehensive use cases for various commands within the project.[#33](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/33)
+ * Authored documentation for the proposed enhancement of integrating `frequency of usage tags`.[#349](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/349)
+ * Added documentation for the potential enhancement for adding graphical representations for the `stats` command.[#349](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/349)
+
+* **Contributions to team-based tasks**:
+ * Ensured code quality is maintained throughout the project
+ * Actively contributed to version-specific milestones, playing a key role in meeting project deadlines and achieving set benchmarks efficiently.
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): [#179](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/179), [#159](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/159)
+ * Contributed to the bug finding for other teams as well, reported bugs can be found [here](https://github.com/D-Limiter/ped/issues)
diff --git a/docs/team/itsnattan.md b/docs/team/itsnattan.md
new file mode 100644
index 00000000000..f77e30f9ca0
--- /dev/null
+++ b/docs/team/itsnattan.md
@@ -0,0 +1,51 @@
+---
+layout: page
+title: Nathanael M. Tan's Project Portfolio Page
+---
+
+### Project: Flashlingo
+
+FlashLingo is a desktop application to facilitate the learning of new languages through flashcards. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**: UI "yes", "no" and "reveal/hide" buttons
+ * What it does: Invoke the yes, no and reveal command upon clicking
+ * Justification: Makes it more convenient for users who prefer a GUI interaction and seamlessly integrate it to perform exactly as the CLI command would.
+ * Highlights: In order to integrate it seamlessly to perform exactly as the CLI would, it required some changes to the structure of the architecture, so that the UI would be able to access some other part of the code in order to execute. Furthermore, it would also require the UI to respond synchronously with the execution of the commands, requiring a deeper understanding of JavaFX and the pre-existing code in order to "refresh" for new data.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=itsNatTan&breakdown=false)
+
+* **Project management**:
+ * Watched deadlines, ensured that internal deadlines were met so that progress was smooth.
+ * Kept a close watch on code quality, ensuring that proper coding standards were adhered to.
+ * Interacted closely with TA and Prof to make sure that certain things are allowed and that we are doing things right.
+
+* **Enhancements to existing features**:
+ * Changed the UI to accept FlashCards instead of People (Adapted to our project)
+ * Added critical functionality to the UI to make things work smoothly and is easy to use.
+ * Add some tests for the flashcard model to ensure that it is working as intended
+
+* **Documentation**:
+ * User Guide:
+ * Created the UGDraft.md file, and added the documentation for the help, exit, save, load functions.
+ * Create the Table of Content, and designed the notation for command to ensure that users will have clarity when deciphering the unfamiliar formats.
+ * Ensured that all command formats followed the proper standardised notation for ease of use.
+ * Update Quick Start section to accommodate less technologically savvy users as well as a section on how to use the application.
+ * Ensured all hyperlinks work as intended.
+ * Developer Guide:
+ * Created User Stories section.
+ * Added some extensions to Use Case section.
+ * Created sequence diagrams for the UI buttons
+ * Changed some UML diagrams to adapt to our current program
+
+* **Contributions to team-based tasks**:
+ * Ensuring coding standard is followed
+ * Managing the GitHub repository
+ * Issue creation and assignment
+ * Assisted in refactoring fragments of code left behind by AB3 into FlashLingo
+ * Extensive bug-hunting throughout the creation of the product
+
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): [#324](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/324#pullrequestreview-1722643494), [#194](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/194)
+ * Put in full effort in bug hunting during the PE-D, finding a total of 15 bugs for the other team.
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index 773a07794e2..00000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-layout: page
-title: John Doe's Project Portfolio Page
----
-
-### Project: AddressBook Level 3
-
-AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
-
-Given below are my contributions to the project.
-
-* **New Feature**: Added the ability to undo/redo previous commands.
- * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command.
- * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them.
- * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands.
- * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}*
-
-* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys.
-
-* **Code contributed**: [RepoSense link]()
-
-* **Project management**:
- * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub
-
-* **Enhancements to existing features**:
- * Updated the GUI color scheme (Pull requests [\#33](), [\#34]())
- * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]())
-
-* **Documentation**:
- * User Guide:
- * Added documentation for the features `delete` and `find` [\#72]()
- * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]()
- * Developer Guide:
- * Added implementation details of the `delete` feature.
-
-* **Community**:
- * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]()
- * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]())
- * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]())
- * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]())
-
-* **Tools**:
- * Integrated a third party library (Natty) to the project ([\#42]())
- * Integrated a new Github plugin (CircleCI) to the team repo
-
-* _{you can add/remove categories in the list above}_
diff --git a/docs/team/song-mengfei.md b/docs/team/song-mengfei.md
new file mode 100644
index 00000000000..0ec83b7ae1b
--- /dev/null
+++ b/docs/team/song-mengfei.md
@@ -0,0 +1,42 @@
+---
+layout: page
+title: Song Mengfei's Project Portfolio Page
+---
+
+### Project: Flashlingo
+
+FlashLingo is a desktop application to facilitate the learning of new languages through flashcards. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**:
+ * Added the ability to update the proficiency level of reviewed flash cards.
+ * What it does: It receives the user's feedback on the flash card and updates the proficiency level of the flash card accordingly.
+ * Justification: The implementation of `Yes` and `No` commands allows users to actively review words, thereby enhancing their memory retention.
+ * Highlights: This upgrade impacts both current and future commands, demanding an in-depth analysis of design options. Integrating the Singleton pattern posed challenges, necessitating complex adjustments to existing commands.
+ * Added the ability to filter flash cards by their language or review date.
+ * What it does: It lists all flash cards with the specified language or the review date before the given date.
+ * Justification: The implementation of `Review` and `Language` command allows users to view all flash cards in the flash card list.
+ * Highlights: This upgrade impacts both current and future commands, demanding an in-depth analysis of design options. Integrating the Singleton pattern posed challenges, necessitating complex adjustments to existing commands.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=song-mengfei&breakdown=true)
+
+* **Enhancements to existing features**:
+ * Update `FindCommand` to support searching by `KEYWORD` [#83](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/83) [#89](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/89)
+ * Update `AddCommand`, `AddCommandParser` and their test. Users can add flash cards with optional parameters`WordLanguage` and `TranslationLanguage` [#161](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/161) [#302](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/302)
+ * Update `EditCommand`, `EditCommandParser` and their test. Users can edit a flash card with at least one and at most four parameters[#216](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/216) [#305](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/305)
+ * Update existing test file [#173](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/173)
+ * Fix checkstyle issues
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the features `add`, `delete`, `edit`, `find`, `language` and `switch`: [#202](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/202) [#339](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/339)
+ * Developer Guide:
+ * Update the design of architecture to adopt our current program
+ * Added implementation details of the features `yes` and `no` commands and added YesCommandActivityDiagram and YesSequenceDiagram: [#176](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/176) [#335](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/335)
+
+* **Contributions to team-based tasks**:
+ * Ensuring coding standard is followed
+ * Issue creation and assignment
+* **Community**:
+ * PRs reviewed (with non-trivial review comments):[#318](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/318)
diff --git a/docs/team/wangcheng0116.md b/docs/team/wangcheng0116.md
new file mode 100644
index 00000000000..7b1201aedfa
--- /dev/null
+++ b/docs/team/wangcheng0116.md
@@ -0,0 +1,41 @@
+---
+layout: page
+title: Wang Cheng's Project Portfolio Page
+---
+
+### Project: Flashlingo
+
+FlashLingo is a desktop application to facilitate the learning of new languages through flashcards. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added the ability to start and end review sessions.
+ * What it does: it allows users to manage review sessions where they can review their flashcards to improve their memory retention.
+ * Justification: The implementation of 'start' and 'end' review session capabilities allows users to actively review words, thereby enhancing their memory retention.
+ * Highlights: This upgrade impacts both current and future commands, demanding an in-depth analysis of design options. Integrating the Singleton pattern posed challenges, necessitating complex adjustments to existing commands.
+
+* **New Feature**: Added the ability to reveal the translation of the particular flash card
+ * What it does: It shows/hides the translation of the particular flash card.
+ * Justification: The implementation of 'reveal' allows users to test their grasp of words, hence enhancing their memory retention.
+ * Highlights: Unlike other commands, `reveal` command has default value when the parameter is omitted. In addition, in order to achieve the same effect of pressing buttons, it requires seamless coordination between command and UI.
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=wangcheng0116&breakdown=false&sort=groupTitle%20dsc&sortWithin=title&since=2023-09-22&timeframe=commit&mergegroup=&groupSelect=groupByRepos#/)
+
+* **Documentation**:
+ * User Guide:
+ * added documentation for the features `yes`, `no`, `start` and `stop`: [#185](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/185)
+ * modified introduction section to briefly explain scientific basis of this application
+ * included Application Features to introduce how the application can be used in a high-level manner: [#306](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/306)
+ * added glossary section to explain the jargon used in the user guide: [#304](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/304/files)
+ * Developer Guide:
+ * added implementation details of the features `start` and `stop` commands: [#175](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/175)
+ * wrote planned enhancement
+
+* **Contributions to team-based tasks**:
+ * Milestone v1.3 creation
+ * Bug issues assignment
+ * Ensuring coding standard is followed
+
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): [#197](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/197), [#311](https://github.com/AY2324S1-CS2103T-T11-4/tp/pull/311)
+ * Reported bugs and suggestions for other teams in the class: [here](https://github.com/WangCheng0116/ped/issues)
+
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
deleted file mode 100644
index 92cd8fa605a..00000000000
--- a/src/main/java/seedu/address/logic/Logic.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package seedu.address.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;
-
-/**
- * API of the Logic component
- */
-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.
- * @throws ParseException If an error occurs during parsing.
- */
- CommandResult execute(String commandText) throws CommandException, ParseException;
-
- /**
- * Returns the AddressBook.
- *
- * @see seedu.address.model.Model#getAddressBook()
- */
- ReadOnlyAddressBook getAddressBook();
-
- /** Returns an unmodifiable view of the filtered list of persons */
- ObservableList getFilteredPersonList();
-
- /**
- * Returns the user prefs' address book file path.
- */
- Path getAddressBookFilePath();
-
- /**
- * Returns the user prefs' GUI settings.
- */
- GuiSettings getGuiSettings();
-
- /**
- * Set the user prefs' GUI settings.
- */
- void setGuiSettings(GuiSettings guiSettings);
-}
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
deleted file mode 100644
index ecd32c31b53..00000000000
--- a/src/main/java/seedu/address/logic/Messages.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package seedu.address.logic;
-
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import seedu.address.logic.parser.Prefix;
-import seedu.address.model.person.Person;
-
-/**
- * Container for user visible messages.
- */
-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_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
- public static final String MESSAGE_DUPLICATE_FIELDS =
- "Multiple values specified for the following single-valued field(s): ";
-
- /**
- * Returns an error message indicating the duplicate prefixes.
- */
- public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) {
- assert duplicatePrefixes.length > 0;
-
- Set duplicateFields =
- Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet());
-
- return MESSAGE_DUPLICATE_FIELDS + String.join(" ", duplicateFields);
- }
-
- /**
- * Formats the {@code person} for display to the user.
- */
- public static String format(Person person) {
- final StringBuilder builder = new StringBuilder();
- builder.append(person.getName())
- .append("; Phone: ")
- .append(person.getPhone())
- .append("; Email: ")
- .append(person.getEmail())
- .append("; Address: ")
- .append(person.getAddress())
- .append("; Tags: ");
- person.getTags().forEach(builder::append);
- return builder.toString();
- }
-
-}
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 5d7185a9680..00000000000
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ /dev/null
@@ -1,84 +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.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-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, Messages.format(toAdd)));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof AddCommand)) {
- return false;
- }
-
- AddCommand otherAddCommand = (AddCommand) other;
- return toAdd.equals(otherAddCommand.toAdd);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("toAdd", toAdd)
- .toString();
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
deleted file mode 100644
index 9c86b1fa6e4..00000000000
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import seedu.address.model.AddressBook;
-import seedu.address.model.Model;
-
-/**
- * Clears the address book.
- */
-public class ClearCommand extends Command {
-
- public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
-
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.setAddressBook(new AddressBook());
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
deleted file mode 100644
index 4b581c7331e..00000000000
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ /dev/null
@@ -1,242 +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 static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.CollectionUtil;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-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;
-
-/**
- * Edits the details of an existing person in the address book.
- */
-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_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.";
-
- private final Index index;
- private final EditPersonDescriptor editPersonDescriptor;
-
- /**
- * @param index of the person in the filtered person list to edit
- * @param editPersonDescriptor details to edit the person with
- */
- public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
- requireNonNull(index);
- requireNonNull(editPersonDescriptor);
-
- this.index = index;
- this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor);
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
-
- if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
-
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
- }
-
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
- }
-
- /**
- * Creates and returns a {@code Person} with the details of {@code personToEdit}
- * edited with {@code 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());
- Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
-
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditCommand)) {
- return false;
- }
-
- EditCommand otherEditCommand = (EditCommand) other;
- return index.equals(otherEditCommand.index)
- && editPersonDescriptor.equals(otherEditCommand.editPersonDescriptor);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("index", index)
- .add("editPersonDescriptor", editPersonDescriptor)
- .toString();
- }
-
- /**
- * Stores the details to edit the person with. Each non-empty field value will replace the
- * corresponding field value of the person.
- */
- public static class EditPersonDescriptor {
- private Name name;
- private Phone phone;
- private Email email;
- private Address address;
- private Set tags;
-
- public EditPersonDescriptor() {}
-
- /**
- * Copy constructor.
- * A defensive copy of {@code tags} is used internally.
- */
- public EditPersonDescriptor(EditPersonDescriptor toCopy) {
- setName(toCopy.name);
- setPhone(toCopy.phone);
- setEmail(toCopy.email);
- setAddress(toCopy.address);
- setTags(toCopy.tags);
- }
-
- /**
- * Returns true if at least one field is edited.
- */
- public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
- }
-
- public void setName(Name name) {
- this.name = name;
- }
-
- public Optional getName() {
- return Optional.ofNullable(name);
- }
-
- public void setPhone(Phone phone) {
- this.phone = phone;
- }
-
- public Optional getPhone() {
- return Optional.ofNullable(phone);
- }
-
- public void setEmail(Email email) {
- this.email = email;
- }
-
- public Optional getEmail() {
- return Optional.ofNullable(email);
- }
-
- public void setAddress(Address address) {
- this.address = address;
- }
-
- public Optional getAddress() {
- return Optional.ofNullable(address);
- }
-
- /**
- * Sets {@code tags} to this object's {@code tags}.
- * A defensive copy of {@code tags} is used internally.
- */
- 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.
- */
- public Optional> getTags() {
- return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditPersonDescriptor)) {
- return false;
- }
-
- EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other;
- return Objects.equals(name, otherEditPersonDescriptor.name)
- && Objects.equals(phone, otherEditPersonDescriptor.phone)
- && Objects.equals(email, otherEditPersonDescriptor.email)
- && Objects.equals(address, otherEditPersonDescriptor.address)
- && Objects.equals(tags, otherEditPersonDescriptor.tags);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
- }
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
deleted file mode 100644
index 84be6ad2596..00000000000
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-
-import seedu.address.model.Model;
-
-/**
- * Lists all persons in the address book to the user.
- */
-public class ListCommand extends Command {
-
- public static final String COMMAND_WORD = "list";
-
- public static final String MESSAGE_SUCCESS = "Listed all persons";
-
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
deleted file mode 100644
index 4ff1a97ed77..00000000000
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.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 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;
-
-/**
- * Parses input arguments and creates a new AddCommand object
- */
-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 {
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
-
- 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));
- }
-
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
- Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
- Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
- Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
- Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get());
- Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
-
- Person person = new Person(name, phone, email, address, tagList);
-
- return new AddCommand(person);
- }
-
- /**
- * 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());
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
deleted file mode 100644
index 3149ee07e0b..00000000000
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
-
-import java.util.logging.Logger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import seedu.address.commons.core.LogsCenter;
-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;
-
-/**
- * Parses user input.
- */
-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 Logger logger = LogsCenter.getLogger(AddressBookParser.class);
-
- /**
- * Parses user input into command for execution.
- *
- * @param userInput full user input string
- * @return the command based on the user input
- * @throws ParseException if the user input does not conform the expected format
- */
- 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));
- }
-
- final String commandWord = matcher.group("commandWord");
- final String arguments = matcher.group("arguments");
-
- // Note to developers: Change the log level in config.json to enable lower level (i.e., FINE, FINER and lower)
- // log messages such as the one below.
- // Lower level log messages are used sparingly to minimize noise in the code.
- logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);
-
- switch (commandWord) {
-
- case AddCommand.COMMAND_WORD:
- return new AddCommandParser().parse(arguments);
-
- case EditCommand.COMMAND_WORD:
- return new EditCommandParser().parse(arguments);
-
- case DeleteCommand.COMMAND_WORD:
- return new DeleteCommandParser().parse(arguments);
-
- case ClearCommand.COMMAND_WORD:
- return new ClearCommand();
-
- case FindCommand.COMMAND_WORD:
- return new FindCommandParser().parse(arguments);
-
- case ListCommand.COMMAND_WORD:
- return new ListCommand();
-
- case ExitCommand.COMMAND_WORD:
- return new ExitCommand();
-
- case HelpCommand.COMMAND_WORD:
- return new HelpCommand();
-
- default:
- logger.finer("This user input caused a ParseException: " + userInput);
- throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
- }
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
deleted file mode 100644
index 75b1a9bf119..00000000000
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package seedu.address.logic.parser;
-
-/**
- * Contains Command Line Interface (CLI) syntax definitions common to multiple commands
- */
-public class CliSyntax {
-
- /* Prefix definitions */
- public static final Prefix PREFIX_NAME = new Prefix("n/");
- public static final Prefix PREFIX_PHONE = new Prefix("p/");
- public static final Prefix PREFIX_EMAIL = new Prefix("e/");
- public static final Prefix PREFIX_ADDRESS = new Prefix("a/");
- public static final Prefix PREFIX_TAG = new Prefix("t/");
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
deleted file mode 100644
index 46b3309a78b..00000000000
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package seedu.address.logic.parser;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.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 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;
-
-/**
- * Parses input arguments and creates a new EditCommand object
- */
-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 {
- requireNonNull(args);
- ArgumentMultimap argMultimap =
- 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);
- }
-
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
-
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
-
- if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
- editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
- }
- if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
- editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
- }
- if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
- editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
- }
- if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
- editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
- }
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
-
- if (!editPersonDescriptor.isAnyFieldEdited()) {
- throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
- }
-
- return new EditCommand(index, editPersonDescriptor);
- }
-
- /**
- * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty.
- * If {@code tags} contain only one element which is an empty string, it will be parsed into a
- * {@code Set} containing zero tags.
- */
- private Optional> parseTagsForEdit(Collection tags) throws ParseException {
- assert tags != null;
-
- if (tags.isEmpty()) {
- return Optional.empty();
- }
- 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/seedu/address/logic/parser/FindCommandParser.java
deleted file mode 100644
index 2867bde857b..00000000000
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.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;
-
-/**
- * Parses input arguments and creates a new FindCommand object
- */
-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 {
- String trimmedArgs = args.trim();
- if (trimmedArgs.isEmpty()) {
- throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
- }
-
- String[] nameKeywords = trimmedArgs.split("\\s+");
-
- return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
deleted file mode 100644
index b117acb9c55..00000000000
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package seedu.address.logic.parser;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.Collection;
-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;
-
-/**
- * Contains utility methods used for parsing strings in the various *Parser classes.
- */
-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.
- * @throws ParseException if the specified index is invalid (not non-zero unsigned integer).
- */
- public static Index parseIndex(String oneBasedIndex) throws ParseException {
- String trimmedIndex = oneBasedIndex.trim();
- if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
- throw new ParseException(MESSAGE_INVALID_INDEX);
- }
- return Index.fromOneBased(Integer.parseInt(trimmedIndex));
- }
-
- /**
- * 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.
- */
- public static Name parseName(String name) throws ParseException {
- requireNonNull(name);
- String trimmedName = name.trim();
- if (!Name.isValidName(trimmedName)) {
- throw new ParseException(Name.MESSAGE_CONSTRAINTS);
- }
- return new Name(trimmedName);
- }
-
- /**
- * 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.
- */
- public static Phone parsePhone(String phone) throws ParseException {
- requireNonNull(phone);
- String trimmedPhone = phone.trim();
- if (!Phone.isValidPhone(trimmedPhone)) {
- throw new ParseException(Phone.MESSAGE_CONSTRAINTS);
- }
- return new Phone(trimmedPhone);
- }
-
- /**
- * 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.
- */
- public static Address parseAddress(String address) throws ParseException {
- requireNonNull(address);
- String trimmedAddress = address.trim();
- if (!Address.isValidAddress(trimmedAddress)) {
- throw new ParseException(Address.MESSAGE_CONSTRAINTS);
- }
- return new Address(trimmedAddress);
- }
-
- /**
- * 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.
- */
- public static Email parseEmail(String email) throws ParseException {
- requireNonNull(email);
- String trimmedEmail = email.trim();
- if (!Email.isValidEmail(trimmedEmail)) {
- throw new ParseException(Email.MESSAGE_CONSTRAINTS);
- }
- return new Email(trimmedEmail);
- }
-
- /**
- * 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.
- */
- public static Tag parseTag(String tag) throws ParseException {
- requireNonNull(tag);
- String trimmedTag = tag.trim();
- if (!Tag.isValidTagName(trimmedTag)) {
- throw new ParseException(Tag.MESSAGE_CONSTRAINTS);
- }
- return new Tag(trimmedTag);
- }
-
- /**
- * Parses {@code Collection tags} into a {@code Set}.
- */
- public static Set parseTags(Collection tags) throws ParseException {
- requireNonNull(tags);
- final Set tagSet = new HashSet<>();
- for (String tagName : tags) {
- tagSet.add(parseTag(tagName));
- }
- return tagSet;
- }
-}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
deleted file mode 100644
index 73397161e84..00000000000
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package seedu.address.model;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-
-import javafx.collections.ObservableList;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.model.person.Person;
-import seedu.address.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 new ToStringBuilder(this)
- .add("persons", persons)
- .toString();
- }
-
- @Override
- public ObservableList getPersonList() {
- return persons.asUnmodifiableObservableList();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof AddressBook)) {
- return false;
- }
-
- AddressBook otherAddressBook = (AddressBook) other;
- return persons.equals(otherAddressBook.persons);
- }
-
- @Override
- public int hashCode() {
- return persons.hashCode();
- }
-}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
deleted file mode 100644
index d54df471c1f..00000000000
--- a/src/main/java/seedu/address/model/Model.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package seedu.address.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;
-
-/**
- * The API of the Model component.
- */
-public interface Model {
- /** {@code Predicate} that always evaluate to true */
- Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
-
- /**
- * Replaces user prefs data with the data in {@code userPrefs}.
- */
- void setUserPrefs(ReadOnlyUserPrefs userPrefs);
-
- /**
- * Returns the user prefs.
- */
- ReadOnlyUserPrefs getUserPrefs();
-
- /**
- * Returns the user prefs' GUI settings.
- */
- GuiSettings getGuiSettings();
-
- /**
- * Sets the user prefs' GUI settings.
- */
- void setGuiSettings(GuiSettings guiSettings);
-
- /**
- * Returns the user prefs' address book file path.
- */
- Path getAddressBookFilePath();
-
- /**
- * Sets the user prefs' address book file path.
- */
- void setAddressBookFilePath(Path addressBookFilePath);
-
- /**
- * Replaces address book data with the data in {@code addressBook}.
- */
- void setAddressBook(ReadOnlyAddressBook addressBook);
-
- /** Returns the AddressBook */
- ReadOnlyAddressBook getAddressBook();
-
- /**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
- */
- boolean hasPerson(Person person);
-
- /**
- * 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.
- */
- 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.
- */
- void setPerson(Person target, Person editedPerson);
-
- /** Returns an unmodifiable view of the filtered person list */
- ObservableList getFilteredPersonList();
-
- /**
- * 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/seedu/address/model/ModelManager.java
deleted file mode 100644
index 57bc563fde6..00000000000
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ /dev/null
@@ -1,148 +0,0 @@
-package seedu.address.model;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.nio.file.Path;
-import java.util.function.Predicate;
-import java.util.logging.Logger;
-
-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;
-
-/**
- * Represents the in-memory model of the address book data.
- */
-public class ModelManager implements Model {
- private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
-
- private final AddressBook addressBook;
- private final UserPrefs userPrefs;
- private final FilteredList filteredPersons;
-
- /**
- * Initializes a ModelManager with the given addressBook and userPrefs.
- */
- public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) {
- requireAllNonNull(addressBook, userPrefs);
-
- logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs);
-
- this.addressBook = new AddressBook(addressBook);
- this.userPrefs = new UserPrefs(userPrefs);
- filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
- }
-
- public ModelManager() {
- this(new AddressBook(), new UserPrefs());
- }
-
- //=========== UserPrefs ==================================================================================
-
- @Override
- public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
- requireNonNull(userPrefs);
- this.userPrefs.resetData(userPrefs);
- }
-
- @Override
- public ReadOnlyUserPrefs getUserPrefs() {
- return userPrefs;
- }
-
- @Override
- public GuiSettings getGuiSettings() {
- return userPrefs.getGuiSettings();
- }
-
- @Override
- public void setGuiSettings(GuiSettings guiSettings) {
- requireNonNull(guiSettings);
- userPrefs.setGuiSettings(guiSettings);
- }
-
- @Override
- public Path getAddressBookFilePath() {
- return userPrefs.getAddressBookFilePath();
- }
-
- @Override
- public void setAddressBookFilePath(Path addressBookFilePath) {
- requireNonNull(addressBookFilePath);
- userPrefs.setAddressBookFilePath(addressBookFilePath);
- }
-
- //=========== AddressBook ================================================================================
-
- @Override
- public void setAddressBook(ReadOnlyAddressBook addressBook) {
- this.addressBook.resetData(addressBook);
- }
-
- @Override
- public ReadOnlyAddressBook getAddressBook() {
- return addressBook;
- }
-
- @Override
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return addressBook.hasPerson(person);
- }
-
- @Override
- public void deletePerson(Person target) {
- addressBook.removePerson(target);
- }
-
- @Override
- public void addPerson(Person person) {
- addressBook.addPerson(person);
- updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- }
-
- @Override
- public void setPerson(Person target, Person editedPerson) {
- requireAllNonNull(target, editedPerson);
-
- addressBook.setPerson(target, editedPerson);
- }
-
- //=========== 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;
- }
-
- @Override
- public void updateFilteredPersonList(Predicate predicate) {
- requireNonNull(predicate);
- filteredPersons.setPredicate(predicate);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof ModelManager)) {
- return false;
- }
-
- ModelManager otherModelManager = (ModelManager) other;
- return addressBook.equals(otherModelManager.addressBook)
- && userPrefs.equals(otherModelManager.userPrefs)
- && filteredPersons.equals(otherModelManager.filteredPersons);
- }
-
-}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
deleted file mode 100644
index 6ddc2cd9a29..00000000000
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package seedu.address.model;
-
-import javafx.collections.ObservableList;
-import seedu.address.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/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java
deleted file mode 100644
index 469a2cc9a1e..00000000000
--- a/src/main/java/seedu/address/model/person/Address.java
+++ /dev/null
@@ -1,65 +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 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) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Address)) {
- return false;
- }
-
- Address otherAddress = (Address) other;
- return value.equals(otherAddress.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
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 c62e512bc29..00000000000
--- a/src/main/java/seedu/address/model/person/Email.java
+++ /dev/null
@@ -1,79 +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) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Email)) {
- return false;
- }
-
- Email otherEmail = (Email) other;
- return value.equals(otherEmail.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java
deleted file mode 100644
index 173f15b9b00..00000000000
--- a/src/main/java/seedu/address/model/person/Name.java
+++ /dev/null
@@ -1,67 +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 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) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Name)) {
- return false;
- }
-
- Name otherName = (Name) other;
- return fullName.equals(otherName.fullName);
- }
-
- @Override
- public int hashCode() {
- return fullName.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
deleted file mode 100644
index 62d19be2977..00000000000
--- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package seedu.address.model.person;
-
-import java.util.List;
-import java.util.function.Predicate;
-
-import seedu.address.commons.util.StringUtil;
-import seedu.address.commons.util.ToStringBuilder;
-
-/**
- * Tests that a {@code Person}'s {@code Name} matches any of the keywords given.
- */
-public class NameContainsKeywordsPredicate implements Predicate {
- private final List keywords;
-
- public NameContainsKeywordsPredicate(List keywords) {
- this.keywords = keywords;
- }
-
- @Override
- public boolean test(Person person) {
- return keywords.stream()
- .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof NameContainsKeywordsPredicate)) {
- return false;
- }
-
- NameContainsKeywordsPredicate otherNameContainsKeywordsPredicate = (NameContainsKeywordsPredicate) other;
- return keywords.equals(otherNameContainsKeywordsPredicate.keywords);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this).add("keywords", keywords).toString();
- }
-}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
deleted file mode 100644
index abe8c46b535..00000000000
--- a/src/main/java/seedu/address/model/person/Person.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package seedu.address.model.person;
-
-import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.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;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Person)) {
- return false;
- }
-
- Person otherPerson = (Person) other;
- return name.equals(otherPerson.name)
- && phone.equals(otherPerson.phone)
- && email.equals(otherPerson.email)
- && address.equals(otherPerson.address)
- && tags.equals(otherPerson.tags);
- }
-
- @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() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/person/Phone.java
deleted file mode 100644
index d733f63d739..00000000000
--- a/src/main/java/seedu/address/model/person/Phone.java
+++ /dev/null
@@ -1,61 +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 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) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Phone)) {
- return false;
- }
-
- Phone otherPhone = (Phone) other;
- return value.equals(otherPhone.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java
deleted file mode 100644
index cc0a68d79f9..00000000000
--- a/src/main/java/seedu/address/model/person/UniquePersonList.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package seedu.address.model.person;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.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;
-
-/**
- * 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) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof UniquePersonList)) {
- return false;
- }
-
- UniquePersonList otherUniquePersonList = (UniquePersonList) other;
- return internalList.equals(otherUniquePersonList.internalList);
- }
-
- @Override
- public int hashCode() {
- return internalList.hashCode();
- }
-
- @Override
- public String toString() {
- return internalList.toString();
- }
-
- /**
- * 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/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
deleted file mode 100644
index d7290f59442..00000000000
--- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package seedu.address.model.person.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() {
- super("Operation would result in duplicate persons");
- }
-}
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/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java
deleted file mode 100644
index f1a0d4e233b..00000000000
--- a/src/main/java/seedu/address/model/tag/Tag.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package seedu.address.model.tag;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.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) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Tag)) {
- return false;
- }
-
- Tag otherTag = (Tag) other;
- return tagName.equals(otherTag.tagName);
- }
-
- @Override
- public int hashCode() {
- return tagName.hashCode();
- }
-
- /**
- * Format state as text for viewing.
- */
- public String toString() {
- return '[' + tagName + ']';
- }
-
-}
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/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java
deleted file mode 100644
index f2e015105ae..00000000000
--- a/src/main/java/seedu/address/storage/AddressBookStorage.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package seedu.address.storage;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.ReadOnlyAddressBook;
-
-/**
- * Represents a storage for {@link seedu.address.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 DataLoadingException if loading the data from storage failed.
- */
- Optional readAddressBook() throws DataLoadingException;
-
- /**
- * @see #getAddressBookFilePath()
- */
- Optional readAddressBook(Path filePath) throws DataLoadingException;
-
- /**
- * 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/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
deleted file mode 100644
index bd1ca0f56c8..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package seedu.address.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 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;
-
-/**
- * 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 tags = 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("tags") List tags) {
- this.name = name;
- this.phone = phone;
- this.email = email;
- this.address = address;
- if (tags != null) {
- this.tags.addAll(tags);
- }
- }
-
- /**
- * 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;
- tags.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 : tags) {
- 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/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java
deleted file mode 100644
index 0df22bdb754..00000000000
--- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package seedu.address.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;
-
-/**
- * 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/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
deleted file mode 100644
index 41e06f264e1..00000000000
--- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package seedu.address.storage;
-
-import static java.util.Objects.requireNonNull;
-
-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.DataLoadingException;
-import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.commons.util.FileUtil;
-import seedu.address.commons.util.JsonUtil;
-import seedu.address.model.ReadOnlyAddressBook;
-
-/**
- * A class to access AddressBook data stored as a json file on the hard disk.
- */
-public class JsonAddressBookStorage implements AddressBookStorage {
-
- private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class);
-
- private Path filePath;
-
- public JsonAddressBookStorage(Path filePath) {
- this.filePath = filePath;
- }
-
- public Path getAddressBookFilePath() {
- return filePath;
- }
-
- @Override
- public Optional readAddressBook() throws DataLoadingException {
- return readAddressBook(filePath);
- }
-
- /**
- * Similar to {@link #readAddressBook()}.
- *
- * @param filePath location of the data. Cannot be null.
- * @throws DataLoadingException if loading the data from storage failed.
- */
- public Optional readAddressBook(Path filePath) throws DataLoadingException {
- requireNonNull(filePath);
-
- Optional jsonAddressBook = JsonUtil.readJsonFile(
- filePath, JsonSerializableAddressBook.class);
- if (!jsonAddressBook.isPresent()) {
- return Optional.empty();
- }
-
- try {
- return Optional.of(jsonAddressBook.get().toModelType());
- } catch (IllegalValueException ive) {
- logger.info("Illegal values found in " + filePath + ": " + ive.getMessage());
- throw new DataLoadingException(ive);
- }
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, filePath);
- }
-
- /**
- * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}.
- *
- * @param filePath location of the data. Cannot be null.
- */
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
- requireNonNull(addressBook);
- requireNonNull(filePath);
-
- FileUtil.createIfMissing(filePath);
- JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath);
- }
-
-}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
deleted file mode 100644
index 5efd834091d..00000000000
--- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.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 seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.AddressBook;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.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/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java
deleted file mode 100644
index 9fba0c7a1d6..00000000000
--- a/src/main/java/seedu/address/storage/Storage.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package seedu.address.storage;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Optional;
-
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
-
-/**
- * API of the Storage component
- */
-public interface Storage extends AddressBookStorage, UserPrefsStorage {
-
- @Override
- Optional readUserPrefs() throws DataLoadingException;
-
- @Override
- void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException;
-
- @Override
- Path getAddressBookFilePath();
-
- @Override
- Optional readAddressBook() throws DataLoadingException;
-
- @Override
- void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException;
-
-}
diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java
deleted file mode 100644
index 8b84a9024d5..00000000000
--- a/src/main/java/seedu/address/storage/StorageManager.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package seedu.address.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.DataLoadingException;
-import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
-
-/**
- * Manages storage of AddressBook data in local storage.
- */
-public class StorageManager implements Storage {
-
- private static final Logger logger = LogsCenter.getLogger(StorageManager.class);
- private AddressBookStorage addressBookStorage;
- private UserPrefsStorage userPrefsStorage;
-
- /**
- * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}.
- */
- public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) {
- this.addressBookStorage = addressBookStorage;
- this.userPrefsStorage = userPrefsStorage;
- }
-
- // ================ UserPrefs methods ==============================
-
- @Override
- public Path getUserPrefsFilePath() {
- return userPrefsStorage.getUserPrefsFilePath();
- }
-
- @Override
- public Optional readUserPrefs() throws DataLoadingException {
- return userPrefsStorage.readUserPrefs();
- }
-
- @Override
- public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException {
- userPrefsStorage.saveUserPrefs(userPrefs);
- }
-
-
- // ================ AddressBook methods ==============================
-
- @Override
- public Path getAddressBookFilePath() {
- return addressBookStorage.getAddressBookFilePath();
- }
-
- @Override
- public Optional readAddressBook() throws DataLoadingException {
- return readAddressBook(addressBookStorage.getAddressBookFilePath());
- }
-
- @Override
- public Optional readAddressBook(Path filePath) throws DataLoadingException {
- logger.fine("Attempting to read data from file: " + filePath);
- return addressBookStorage.readAddressBook(filePath);
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException {
- saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath());
- }
-
- @Override
- public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException {
- logger.fine("Attempting to write to data file: " + filePath);
- addressBookStorage.saveAddressBook(addressBook, filePath);
- }
-
-}
diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java
deleted file mode 100644
index 094c42cda82..00000000000
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package seedu.address.ui;
-
-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 seedu.address.model.person.Person;
-
-/**
- * An UI component that displays information of a {@code Person}.
- */
-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.
- *
- * @see The issue on AddressBook level 4
- */
-
- public final Person person;
-
- @FXML
- private HBox cardPane;
- @FXML
- private Label name;
- @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 PersonCard(Person person, int displayedIndex) {
- super(FXML);
- this.person = person;
- id.setText(displayedIndex + ". ");
- name.setText(person.getName().fullName);
- phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
- email.setText(person.getEmail().value);
- person.getTags().stream()
- .sorted(Comparator.comparing(tag -> tag.tagName))
- .forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
- }
-}
diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java
deleted file mode 100644
index f4c501a897b..00000000000
--- a/src/main/java/seedu/address/ui/PersonListPanel.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package seedu.address.ui;
-
-import java.util.logging.Logger;
-
-import javafx.collections.ObservableList;
-import javafx.fxml.FXML;
-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;
-
-/**
- * Panel containing the list of persons.
- */
-public class PersonListPanel extends UiPart {
- private static final String FXML = "PersonListPanel.fxml";
- private final Logger logger = LogsCenter.getLogger(PersonListPanel.class);
-
- @FXML
- private ListView personListView;
-
- /**
- * Creates a {@code PersonListPanel} with the given {@code ObservableList}.
- */
- public PersonListPanel(ObservableList personList) {
- super(FXML);
- personListView.setItems(personList);
- personListView.setCellFactory(listView -> new PersonListViewCell());
- }
-
- /**
- * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}.
- */
- class PersonListViewCell extends ListCell {
- @Override
- protected void updateItem(Person 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/seedu/address/AppParameters.java b/src/main/java/seedu/flashlingo/AppParameters.java
similarity index 91%
rename from src/main/java/seedu/address/AppParameters.java
rename to src/main/java/seedu/flashlingo/AppParameters.java
index 3d603622d4e..9d1202c29ea 100644
--- a/src/main/java/seedu/address/AppParameters.java
+++ b/src/main/java/seedu/flashlingo/AppParameters.java
@@ -1,4 +1,4 @@
-package seedu.address;
+package seedu.flashlingo;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -7,9 +7,9 @@
import java.util.logging.Logger;
import javafx.application.Application;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.util.FileUtil;
-import seedu.address.commons.util.ToStringBuilder;
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.commons.util.FileUtil;
+import seedu.flashlingo.commons.util.ToStringBuilder;
/**
* Represents the parsed command-line parameters given to the application.
diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/flashlingo/Main.java
similarity index 95%
rename from src/main/java/seedu/address/Main.java
rename to src/main/java/seedu/flashlingo/Main.java
index ec1b7958746..e54e41f8d83 100644
--- a/src/main/java/seedu/address/Main.java
+++ b/src/main/java/seedu/flashlingo/Main.java
@@ -1,9 +1,9 @@
-package seedu.address;
+package seedu.flashlingo;
import java.util.logging.Logger;
import javafx.application.Application;
-import seedu.address.commons.core.LogsCenter;
+import seedu.flashlingo.commons.core.LogsCenter;
/**
* The main entry point to the application.
@@ -34,7 +34,6 @@ public static void main(String[] args) {
// The warning however, can be safely ignored. Thus, the following log informs
// the user (if looking at the log output) that the said warning appearing in the log
// can be ignored.
-
logger.warning("The warning about Unsupported JavaFX configuration below can be ignored.");
Application.launch(MainApp.class, args);
}
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/flashlingo/MainApp.java
similarity index 63%
rename from src/main/java/seedu/address/MainApp.java
rename to src/main/java/seedu/flashlingo/MainApp.java
index 3d6bd06d5af..88e396f7a75 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/flashlingo/MainApp.java
@@ -1,4 +1,5 @@
-package seedu.address;
+//@@author
+package seedu.flashlingo;
import java.io.IOException;
import java.nio.file.Path;
@@ -7,37 +8,36 @@
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.DataLoadingException;
-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 seedu.flashlingo.commons.core.Config;
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.commons.core.Version;
+import seedu.flashlingo.commons.exceptions.DataLoadingException;
+import seedu.flashlingo.commons.util.ConfigUtil;
+import seedu.flashlingo.commons.util.StringUtil;
+import seedu.flashlingo.logic.Logic;
+import seedu.flashlingo.logic.LogicManager;
+import seedu.flashlingo.model.Flashlingo;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.ModelManager;
+import seedu.flashlingo.model.ReadOnlyFlashlingo;
+import seedu.flashlingo.model.ReadOnlyUserPrefs;
+import seedu.flashlingo.model.UserPrefs;
+import seedu.flashlingo.model.util.SampleDataUtil;
+import seedu.flashlingo.storage.FlashlingoStorage;
+import seedu.flashlingo.storage.JsonFlashlingoStorage;
+import seedu.flashlingo.storage.JsonUserPrefsStorage;
+import seedu.flashlingo.storage.Storage;
+import seedu.flashlingo.storage.StorageManager;
+import seedu.flashlingo.storage.UserPrefsStorage;
+import seedu.flashlingo.ui.Ui;
+import seedu.flashlingo.ui.UiManager;
+
+//@@author
/**
* Runs the application.
*/
public class MainApp extends Application {
-
public static final Version VERSION = new Version(0, 2, 2, true);
-
private static final Logger logger = LogsCenter.getLogger(MainApp.class);
protected Ui ui;
@@ -48,7 +48,7 @@ public class MainApp extends Application {
@Override
public void init() throws Exception {
- logger.info("=============================[ Initializing AddressBook ]===========================");
+ logger.info("=============================[ Initializing Flashlingo ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -57,37 +57,38 @@ public void init() throws Exception {
UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath());
UserPrefs userPrefs = initPrefs(userPrefsStorage);
- AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath());
- storage = new StorageManager(addressBookStorage, userPrefsStorage);
+ FlashlingoStorage flashlingoStorage = new JsonFlashlingoStorage(userPrefs.getFlashlingoFilePath());
+
+ storage = new StorageManager(flashlingoStorage, userPrefsStorage);
model = initModelManager(storage, userPrefs);
logic = new LogicManager(model, storage);
- ui = new UiManager(logic);
+ ui = new UiManager(logic, model);
}
/**
- * 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 Flashlingo and {@code userPrefs}.
+ * The data from the sample Flashlingo will be used instead if {@code storage}'s Flashlingo is not found,
+ * or an empty Flashlingo will be used instead if errors occur when reading {@code storage}'s Flashlingo.
*/
private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
- logger.info("Using data file : " + storage.getAddressBookFilePath());
+ logger.info("Using data file : " + storage.getFlashlingoFilePath());
- Optional addressBookOptional;
- ReadOnlyAddressBook initialData;
+ Optional flashlingoOptional;
+ ReadOnlyFlashlingo initialData;
try {
- addressBookOptional = storage.readAddressBook();
- if (!addressBookOptional.isPresent()) {
- logger.info("Creating a new data file " + storage.getAddressBookFilePath()
- + " populated with a sample AddressBook.");
+ flashlingoOptional = storage.readFlashlingo();
+ if (!flashlingoOptional.isPresent()) {
+ logger.info("Creating a new data file " + storage.getFlashlingoFilePath()
+ + " populated with a sample Flashlingo.");
}
- initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
+ initialData = flashlingoOptional.orElseGet(SampleDataUtil::getSampleFlashlingo);
} catch (DataLoadingException e) {
- logger.warning("Data file at " + storage.getAddressBookFilePath() + " could not be loaded."
- + " Will be starting with an empty AddressBook.");
- initialData = new AddressBook();
+ logger.warning("Data file at " + storage.getFlashlingoFilePath() + " could not be loaded."
+ + " Will be starting with an empty Flashlingo.");
+ initialData = new Flashlingo();
}
return new ModelManager(initialData, userPrefs);
@@ -149,12 +150,12 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
try {
Optional prefsOptional = storage.readUserPrefs();
if (!prefsOptional.isPresent()) {
- logger.info("Creating new preference file " + prefsFilePath);
+ logger.info("Creating a new preference file " + prefsFilePath);
}
initializedPrefs = prefsOptional.orElse(new UserPrefs());
} catch (DataLoadingException e) {
logger.warning("Preference file at " + prefsFilePath + " could not be loaded."
- + " Using default preferences.");
+ + " Using default preferences now.");
initializedPrefs = new UserPrefs();
}
@@ -170,13 +171,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting Flashlingo " + MainApp.VERSION);
ui.start(primaryStage);
}
@Override
public void stop() {
- logger.info("============================ [ Stopping Address Book ] =============================");
+ logger.info("============================ [ Stopping Flashlingo ] =============================");
try {
storage.saveUserPrefs(model.getUserPrefs());
} catch (IOException e) {
diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/flashlingo/commons/core/Config.java
similarity index 94%
rename from src/main/java/seedu/address/commons/core/Config.java
rename to src/main/java/seedu/flashlingo/commons/core/Config.java
index 485f85a5e05..46e83eb4b9f 100644
--- a/src/main/java/seedu/address/commons/core/Config.java
+++ b/src/main/java/seedu/flashlingo/commons/core/Config.java
@@ -1,11 +1,11 @@
-package seedu.address.commons.core;
+package seedu.flashlingo.commons.core;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.logging.Level;
-import seedu.address.commons.util.ToStringBuilder;
+import seedu.flashlingo.commons.util.ToStringBuilder;
/**
* Config values used by the app
diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/flashlingo/commons/core/GuiSettings.java
similarity index 96%
rename from src/main/java/seedu/address/commons/core/GuiSettings.java
rename to src/main/java/seedu/flashlingo/commons/core/GuiSettings.java
index a97a86ee8d7..fd79fc9ae4e 100644
--- a/src/main/java/seedu/address/commons/core/GuiSettings.java
+++ b/src/main/java/seedu/flashlingo/commons/core/GuiSettings.java
@@ -1,10 +1,10 @@
-package seedu.address.commons.core;
+package seedu.flashlingo.commons.core;
import java.awt.Point;
import java.io.Serializable;
import java.util.Objects;
-import seedu.address.commons.util.ToStringBuilder;
+import seedu.flashlingo.commons.util.ToStringBuilder;
/**
* A Serializable class that contains the GUI settings.
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/flashlingo/commons/core/LogsCenter.java
similarity index 97%
rename from src/main/java/seedu/address/commons/core/LogsCenter.java
rename to src/main/java/seedu/flashlingo/commons/core/LogsCenter.java
index 8cf8e15a0f0..d7e09123e3b 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/seedu/flashlingo/commons/core/LogsCenter.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package seedu.flashlingo.commons.core;
import static java.util.Objects.requireNonNull;
@@ -20,7 +20,7 @@
public class LogsCenter {
private static final int MAX_FILE_COUNT = 5;
private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB
- private static final String LOG_FILE = "addressbook.log";
+ private static final String LOG_FILE = "flashlingo.log";
private static final Logger logger; // logger for this class
private static Logger baseLogger; // to be used as the parent of all other loggers created by this class.
private static Level currentLogLevel = Level.INFO;
diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/flashlingo/commons/core/Version.java
similarity index 98%
rename from src/main/java/seedu/address/commons/core/Version.java
rename to src/main/java/seedu/flashlingo/commons/core/Version.java
index 491d24559b4..d0d17907ae7 100644
--- a/src/main/java/seedu/address/commons/core/Version.java
+++ b/src/main/java/seedu/flashlingo/commons/core/Version.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.core;
+package seedu.flashlingo.commons.core;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/flashlingo/commons/core/index/Index.java
similarity index 94%
rename from src/main/java/seedu/address/commons/core/index/Index.java
rename to src/main/java/seedu/flashlingo/commons/core/index/Index.java
index dd170d8b68d..8a0bd521b4c 100644
--- a/src/main/java/seedu/address/commons/core/index/Index.java
+++ b/src/main/java/seedu/flashlingo/commons/core/index/Index.java
@@ -1,6 +1,6 @@
-package seedu.address.commons.core.index;
+package seedu.flashlingo.commons.core.index;
-import seedu.address.commons.util.ToStringBuilder;
+import seedu.flashlingo.commons.util.ToStringBuilder;
/**
* Represents a zero-based or one-based index.
diff --git a/src/main/java/seedu/address/commons/exceptions/DataLoadingException.java b/src/main/java/seedu/flashlingo/commons/exceptions/DataLoadingException.java
similarity index 81%
rename from src/main/java/seedu/address/commons/exceptions/DataLoadingException.java
rename to src/main/java/seedu/flashlingo/commons/exceptions/DataLoadingException.java
index 9904ba47afe..b0c80732553 100644
--- a/src/main/java/seedu/address/commons/exceptions/DataLoadingException.java
+++ b/src/main/java/seedu/flashlingo/commons/exceptions/DataLoadingException.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.exceptions;
+package seedu.flashlingo.commons.exceptions;
/**
* Represents an error during loading of data from a file.
diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/flashlingo/commons/exceptions/IllegalValueException.java
similarity index 92%
rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
rename to src/main/java/seedu/flashlingo/commons/exceptions/IllegalValueException.java
index 19124db485c..26d8ec7c1ae 100644
--- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
+++ b/src/main/java/seedu/flashlingo/commons/exceptions/IllegalValueException.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.exceptions;
+package seedu.flashlingo.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/seedu/flashlingo/commons/util/AppUtil.java
similarity index 94%
rename from src/main/java/seedu/address/commons/util/AppUtil.java
rename to src/main/java/seedu/flashlingo/commons/util/AppUtil.java
index 87aa89c0326..a65c8621166 100644
--- a/src/main/java/seedu/address/commons/util/AppUtil.java
+++ b/src/main/java/seedu/flashlingo/commons/util/AppUtil.java
@@ -1,9 +1,9 @@
-package seedu.address.commons.util;
+package seedu.flashlingo.commons.util;
import static java.util.Objects.requireNonNull;
import javafx.scene.image.Image;
-import seedu.address.MainApp;
+import seedu.flashlingo.MainApp;
/**
* A container for App specific utility functions
diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/flashlingo/commons/util/CollectionUtil.java
similarity index 96%
rename from src/main/java/seedu/address/commons/util/CollectionUtil.java
rename to src/main/java/seedu/flashlingo/commons/util/CollectionUtil.java
index eafe4dfd681..ac937948f42 100644
--- a/src/main/java/seedu/address/commons/util/CollectionUtil.java
+++ b/src/main/java/seedu/flashlingo/commons/util/CollectionUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.flashlingo.commons.util;
import static java.util.Objects.requireNonNull;
diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/flashlingo/commons/util/ConfigUtil.java
similarity index 76%
rename from src/main/java/seedu/address/commons/util/ConfigUtil.java
rename to src/main/java/seedu/flashlingo/commons/util/ConfigUtil.java
index 7b829c3c4cc..f687cbd92e9 100644
--- a/src/main/java/seedu/address/commons/util/ConfigUtil.java
+++ b/src/main/java/seedu/flashlingo/commons/util/ConfigUtil.java
@@ -1,11 +1,11 @@
-package seedu.address.commons.util;
+package seedu.flashlingo.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.DataLoadingException;
+import seedu.flashlingo.commons.core.Config;
+import seedu.flashlingo.commons.exceptions.DataLoadingException;
/**
* A class for accessing the Config File.
diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/flashlingo/commons/util/FileUtil.java
similarity index 98%
rename from src/main/java/seedu/address/commons/util/FileUtil.java
rename to src/main/java/seedu/flashlingo/commons/util/FileUtil.java
index b1e2767cdd9..e9b76d37eb8 100644
--- a/src/main/java/seedu/address/commons/util/FileUtil.java
+++ b/src/main/java/seedu/flashlingo/commons/util/FileUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.flashlingo.commons.util;
import java.io.IOException;
import java.nio.file.Files;
diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/flashlingo/commons/util/JsonUtil.java
similarity index 97%
rename from src/main/java/seedu/address/commons/util/JsonUtil.java
rename to src/main/java/seedu/flashlingo/commons/util/JsonUtil.java
index 100cb16c395..130ea312609 100644
--- a/src/main/java/seedu/address/commons/util/JsonUtil.java
+++ b/src/main/java/seedu/flashlingo/commons/util/JsonUtil.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.flashlingo.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.DataLoadingException;
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.commons.exceptions.DataLoadingException;
/**
* Converts a Java object instance to JSON and vice versa
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/flashlingo/commons/util/StringUtil.java
similarity index 95%
rename from src/main/java/seedu/address/commons/util/StringUtil.java
rename to src/main/java/seedu/flashlingo/commons/util/StringUtil.java
index 61cc8c9a1cb..5338d8db83b 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/seedu/flashlingo/commons/util/StringUtil.java
@@ -1,7 +1,7 @@
-package seedu.address.commons.util;
+package seedu.flashlingo.commons.util;
import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
+import static seedu.flashlingo.commons.util.AppUtil.checkArgument;
import java.io.PrintWriter;
import java.io.StringWriter;
diff --git a/src/main/java/seedu/address/commons/util/ToStringBuilder.java b/src/main/java/seedu/flashlingo/commons/util/ToStringBuilder.java
similarity index 97%
rename from src/main/java/seedu/address/commons/util/ToStringBuilder.java
rename to src/main/java/seedu/flashlingo/commons/util/ToStringBuilder.java
index d979b926734..33e8d945bfd 100644
--- a/src/main/java/seedu/address/commons/util/ToStringBuilder.java
+++ b/src/main/java/seedu/flashlingo/commons/util/ToStringBuilder.java
@@ -1,4 +1,4 @@
-package seedu.address.commons.util;
+package seedu.flashlingo.commons.util;
/**
* Builds a string representation of an object that is suitable as the return value of {@link Object#toString()}.
diff --git a/src/main/java/seedu/flashlingo/logic/Logic.java b/src/main/java/seedu/flashlingo/logic/Logic.java
new file mode 100644
index 00000000000..734a8520c4a
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/Logic.java
@@ -0,0 +1,58 @@
+//@@author
+package seedu.flashlingo.logic;
+
+import java.nio.file.Path;
+
+import javafx.collections.ObservableList;
+import seedu.flashlingo.commons.core.GuiSettings;
+import seedu.flashlingo.logic.commands.CommandResult;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.ReadOnlyFlashlingo;
+import seedu.flashlingo.model.flashcard.FlashCard;
+
+/**
+ * API of the Logic component
+ */
+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.
+ * @throws ParseException If an error occurs during parsing.
+ */
+ CommandResult execute(String commandText) throws CommandException, ParseException;
+
+ /**
+ * Returns the Flashlingo.
+ *
+ * @see Model#getFlashlingo()
+ */
+ ReadOnlyFlashlingo getFlashlingo();
+
+ /** Returns an unmodifiable view of the filtered list of flashcards */
+ ObservableList getFilteredFlashCardList();
+
+ /**
+ * Returns the user prefs' Flashlingo file path.
+ */
+ Path getFlashlingoFilePath();
+
+ /**
+ * Returns the user prefs' GUI settings.
+ */
+ GuiSettings getGuiSettings();
+
+ /**
+ * Sets the user prefs' GUI settings.
+ */
+ void setGuiSettings(GuiSettings guiSettings);
+
+ //@@author A1WAYSD
+ /**
+ * Returns the user prefs' theme.
+ */
+ String getTheme();
+}
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/flashlingo/logic/LogicManager.java
similarity index 56%
rename from src/main/java/seedu/address/logic/LogicManager.java
rename to src/main/java/seedu/flashlingo/logic/LogicManager.java
index 5aa3b91c7d0..51b98476085 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/flashlingo/logic/LogicManager.java
@@ -1,4 +1,5 @@
-package seedu.address.logic;
+//@@author
+package seedu.flashlingo.logic;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
@@ -6,32 +7,30 @@
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 seedu.flashlingo.commons.core.GuiSettings;
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.logic.commands.Command;
+import seedu.flashlingo.logic.commands.CommandResult;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.logic.parser.FlashlingoParser;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.ReadOnlyFlashlingo;
+import seedu.flashlingo.model.flashcard.FlashCard;
+import seedu.flashlingo.storage.Storage;
/**
* The main LogicManager of the app.
*/
public class LogicManager implements Logic {
public static final String FILE_OPS_ERROR_FORMAT = "Could not save data due to the following error: %s";
-
public static final String FILE_OPS_PERMISSION_ERROR_FORMAT =
"Could not save data to file %s due to insufficient permissions to write to the file or the folder.";
private final Logger logger = LogsCenter.getLogger(LogicManager.class);
-
private final Model model;
private final Storage storage;
- private final AddressBookParser addressBookParser;
+ private final FlashlingoParser flashlingoParser;
/**
* Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}.
@@ -39,19 +38,19 @@ public class LogicManager implements Logic {
public LogicManager(Model model, Storage storage) {
this.model = model;
this.storage = storage;
- addressBookParser = new AddressBookParser();
+ flashlingoParser = new FlashlingoParser();
}
@Override
public CommandResult execute(String commandText) throws CommandException, ParseException {
logger.info("----------------[USER COMMAND][" + commandText + "]");
- CommandResult commandResult;
- Command command = addressBookParser.parseCommand(commandText);
- commandResult = command.execute(model);
+ Command command = flashlingoParser.parseCommand(commandText);
+ CommandResult commandResult = command.execute(model);
try {
- storage.saveAddressBook(model.getAddressBook());
+ storage.saveFlashlingo(model.getFlashlingo());
+ storage.saveUserPrefs(model.getUserPrefs());
} catch (AccessDeniedException e) {
throw new CommandException(String.format(FILE_OPS_PERMISSION_ERROR_FORMAT, e.getMessage()), e);
} catch (IOException ioe) {
@@ -62,18 +61,18 @@ public CommandResult execute(String commandText) throws CommandException, ParseE
}
@Override
- public ReadOnlyAddressBook getAddressBook() {
- return model.getAddressBook();
+ public ReadOnlyFlashlingo getFlashlingo() {
+ return model.getFlashlingo();
}
@Override
- public ObservableList getFilteredPersonList() {
- return model.getFilteredPersonList();
+ public ObservableList getFilteredFlashCardList() {
+ return model.getFilteredFlashCardList();
}
@Override
- public Path getAddressBookFilePath() {
- return model.getAddressBookFilePath();
+ public Path getFlashlingoFilePath() {
+ return model.getFlashlingoFilePath();
}
@Override
@@ -85,4 +84,10 @@ public GuiSettings getGuiSettings() {
public void setGuiSettings(GuiSettings guiSettings) {
model.setGuiSettings(guiSettings);
}
+
+ //@@author A1WAYSD
+ @Override
+ public String getTheme() {
+ return model.getTheme();
+ }
}
diff --git a/src/main/java/seedu/flashlingo/logic/Messages.java b/src/main/java/seedu/flashlingo/logic/Messages.java
new file mode 100644
index 00000000000..30ba9d8324b
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/Messages.java
@@ -0,0 +1,59 @@
+//@@author
+package seedu.flashlingo.logic;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import seedu.flashlingo.logic.parser.Prefix;
+import seedu.flashlingo.model.flashcard.FlashCard;
+
+/**
+ * Container for user visible messages.
+ */
+public class Messages {
+ //@@author A1WAYSD
+ 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_FLASHCARD_DISPLAYED_INDEX = "The flashcard index provided is invalid";
+ public static final String MESSAGE_FLASHCARDS_LISTED_OVERVIEW = "%1$d flashcards listed!\n";
+ public static final String MESSAGE_DUPLICATE_FIELDS =
+ "Multiple values specified for the following single-valued field(s): ";
+ public static final String MESSAGE_DUPLICATE_FLASHCARD = "This flash card already exists in Flashlingo!";
+ public static final String MESSAGE_OPEN_FILE_FAIL = "File not found or accessible.";
+ public static final String MESSAGE_READ_FILE_FAIL = "File cannot be read due to invalid content or format.";
+ //@@author WangCheng0116
+ public static final String MESSAGE_IN_REVIEW_SESSION = "Sorry, currently you are in a review session. Your command "
+ + "is not supported.\nPlease end the review session first.";
+ public static final String MESSAGE_NOT_IN_REVIEW_SESSION = "You are not in a review session.";
+ //@@author Song-Mengfei
+ public static final String MESSAGE_SAME_WORD = "Word and translation should be different.";
+ public static final String MESSAGE_EMPTY_VALUE = "Word/Translation cannot be empty";
+
+ //@@author
+ /**
+ * Returns an error message indicating the duplicate prefixes.
+ */
+ public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePrefixes) {
+ assert duplicatePrefixes.length > 0;
+
+ Set duplicateFields =
+ Stream.of(duplicatePrefixes).map(Prefix::toString).collect(Collectors.toSet());
+
+ return MESSAGE_DUPLICATE_FIELDS + String.join(" ", duplicateFields);
+ }
+
+ //@@author A1WAYSD-reused
+ //Reused method from AB-3 Messages.java with modifications
+ /**
+ * Formats the {@code flashcard} for display to the user.
+ */
+ public static String format(FlashCard flashCard) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(flashCard.getOriginalWord().getWord())
+ .append("; Translated Word: ")
+ .append(flashCard.getTranslatedWord().getWord());
+ return builder.toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/AddCommand.java b/src/main/java/seedu/flashlingo/logic/commands/AddCommand.java
new file mode 100644
index 00000000000..6da9d5e23e3
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/AddCommand.java
@@ -0,0 +1,99 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_ORIGINAL_WORD;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_ORIGINAL_WORD_LANGUAGE;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_TRANSLATED_WORD;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_TRANSLATED_WORD_LANGUAGE;
+
+import java.util.Date;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.flashcard.FlashCard;
+import seedu.flashlingo.model.flashcard.ProficiencyLevel;
+import seedu.flashlingo.model.flashcard.words.OriginalWord;
+import seedu.flashlingo.model.flashcard.words.TranslatedWord;
+
+/**
+ * Adds a flash card to Flashlingo.
+ */
+public class AddCommand extends Command {
+ public static final String COMMAND_WORD = "add";
+
+ // For help function
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a flashcard to Flashlingo.\n"
+ + "Parameters: "
+ + PREFIX_ORIGINAL_WORD + "ORIGINAL WORD "
+ + PREFIX_TRANSLATED_WORD + "TRANSLATION "
+ + "[" + PREFIX_ORIGINAL_WORD_LANGUAGE + "ORIGINAL WORD LANGUAGE] "
+ + "[" + PREFIX_TRANSLATED_WORD_LANGUAGE + "TRANSLATED WORD LANGUAGE] \n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_ORIGINAL_WORD + "hello "
+ + PREFIX_ORIGINAL_WORD_LANGUAGE + "English "
+ + PREFIX_TRANSLATED_WORD + "你好 "
+ + PREFIX_TRANSLATED_WORD_LANGUAGE + "Chinese";
+
+
+ public static final String MESSAGE_SUCCESS = "New flashcard added: %s - %s";
+ public static final String MESSAGE_DUPLICATE_CARD = "This flashcard already exists";
+ private FlashCard toAdd;
+ private OriginalWord original;
+ private TranslatedWord translated;
+
+ /**
+ * Creates an AddCommand to add the specified {@code FlashCard}
+ */
+ public AddCommand(OriginalWord original, TranslatedWord translated) {
+ requireNonNull(original);
+ requireNonNull(translated);
+ this.original = original;
+ this.translated = translated;
+ }
+
+ /**
+ * Creates an AddCommand to add the specified {@code FlashCard}
+ */
+ public AddCommand(FlashCard flashCard) {
+ requireNonNull(flashCard);
+ this.original = flashCard.getOriginalWord();
+ this.translated = flashCard.getTranslatedWord();
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ try {
+ this.toAdd = new FlashCard(original, translated, new Date(), new ProficiencyLevel(1));
+ } catch (IllegalArgumentException iae) {
+ throw new CommandException(iae.getMessage());
+ }
+ if (model.hasFlashCard(toAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_CARD);
+ }
+ model.addFlashCard(toAdd);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, this.original.getWord(), this.translated.getWord()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddCommand)) {
+ return false;
+ }
+
+ AddCommand otherAddCommand = (AddCommand) other;
+ return original.equals(otherAddCommand.original) && translated.equals(otherAddCommand.translated);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/flashlingo/logic/commands/Command.java
similarity index 77%
rename from src/main/java/seedu/address/logic/commands/Command.java
rename to src/main/java/seedu/flashlingo/logic/commands/Command.java
index 64f18992160..09f757ecc3f 100644
--- a/src/main/java/seedu/address/logic/commands/Command.java
+++ b/src/main/java/seedu/flashlingo/logic/commands/Command.java
@@ -1,13 +1,12 @@
-package seedu.address.logic.commands;
+package seedu.flashlingo.logic.commands;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.model.Model;
/**
* Represents a command with hidden internal logic and the ability to be executed.
*/
public abstract class Command {
-
/**
* Executes the command and returns the result message.
*
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/flashlingo/logic/commands/CommandResult.java
similarity index 62%
rename from src/main/java/seedu/address/logic/commands/CommandResult.java
rename to src/main/java/seedu/flashlingo/logic/commands/CommandResult.java
index 249b6072d0d..f3b8b57a4a5 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/flashlingo/logic/commands/CommandResult.java
@@ -1,16 +1,16 @@
-package seedu.address.logic.commands;
+package seedu.flashlingo.logic.commands;
import static java.util.Objects.requireNonNull;
import java.util.Objects;
-import seedu.address.commons.util.ToStringBuilder;
+import seedu.flashlingo.commons.util.ToStringBuilder;
/**
* Represents the result of a command execution.
*/
public class CommandResult {
-
+ /** Feedback message to be shown to the user. */
private final String feedbackToUser;
/** Help information should be shown to the user. */
@@ -19,13 +19,17 @@ public class CommandResult {
/** The application should exit. */
private final boolean exit;
+ /** The application should switch theme. */
+ private final boolean switchTheme;
+
/**
* Constructs a {@code CommandResult} with the specified fields.
*/
- public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
+ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, boolean switchTheme) {
this.feedbackToUser = requireNonNull(feedbackToUser);
this.showHelp = showHelp;
this.exit = exit;
+ this.switchTheme = switchTheme;
}
/**
@@ -33,21 +37,41 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
* and other fields set to their default value.
*/
public CommandResult(String feedbackToUser) {
- this(feedbackToUser, false, false);
+ this(feedbackToUser, false, false, false);
}
+ /**
+ * Constructs a {@code CommandResult} with the specified fields.
+ * @return feedback to user
+ */
public String getFeedbackToUser() {
return feedbackToUser;
}
+ /**
+ * shows whether it is help command
+ * @return whether to show help
+ */
public boolean isShowHelp() {
return showHelp;
}
+ /**
+ * shows whether it is exit command
+ * @return whether to exit
+ */
public boolean isExit() {
return exit;
}
+ /**
+ * Shows whether it is switch theme command.
+ * @return whether to switch theme
+ */
+ public boolean isSwitchTheme() {
+ return switchTheme;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -62,12 +86,13 @@ public boolean equals(Object other) {
CommandResult otherCommandResult = (CommandResult) other;
return feedbackToUser.equals(otherCommandResult.feedbackToUser)
&& showHelp == otherCommandResult.showHelp
- && exit == otherCommandResult.exit;
+ && exit == otherCommandResult.exit
+ && switchTheme == otherCommandResult.switchTheme;
}
@Override
public int hashCode() {
- return Objects.hash(feedbackToUser, showHelp, exit);
+ return Objects.hash(feedbackToUser, showHelp, exit, switchTheme);
}
@Override
@@ -76,6 +101,7 @@ public String toString() {
.add("feedbackToUser", feedbackToUser)
.add("showHelp", showHelp)
.add("exit", exit)
+ .add("switchTheme", switchTheme)
.toString();
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/flashlingo/logic/commands/DeleteCommand.java
similarity index 57%
rename from src/main/java/seedu/address/logic/commands/DeleteCommand.java
rename to src/main/java/seedu/flashlingo/logic/commands/DeleteCommand.java
index 1135ac19b74..f012105e23a 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/flashlingo/logic/commands/DeleteCommand.java
@@ -1,29 +1,28 @@
-package seedu.address.logic.commands;
+package seedu.flashlingo.logic.commands;
import static java.util.Objects.requireNonNull;
import java.util.List;
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Person;
+import seedu.flashlingo.commons.core.index.Index;
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.Messages;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.flashcard.FlashCard;
/**
- * Deletes a person identified using it's displayed index from the address book.
+ * Deletes a flash card identified using it's displayed index from Flashlingo.
*/
public class DeleteCommand extends Command {
-
public static final String COMMAND_WORD = "delete";
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Deletes the person identified by the index number used in the displayed person list.\n"
+ + ": Deletes the word identified by the index number used in the displayed flashcard list.\n"
+ "Parameters: INDEX (must be a positive integer)\n"
+ "Example: " + COMMAND_WORD + " 1";
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
+ public static final String MESSAGE_DELETE_FLASHCARD_SUCCESS = "Deleted Word: %1$s";
private final Index targetIndex;
@@ -34,15 +33,16 @@ public DeleteCommand(Index targetIndex) {
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
+ List lastShownList = model.getFilteredFlashCardList();
if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ throw new CommandException(Messages.MESSAGE_INVALID_FLASHCARD_DISPLAYED_INDEX);
}
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
- model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
+ FlashCard flashCardToDelete = lastShownList.get(targetIndex.getZeroBased());
+ model.deleteFlashCard(flashCardToDelete);
+ return new CommandResult(String.format(MESSAGE_DELETE_FLASHCARD_SUCCESS,
+ Messages.format(flashCardToDelete)));
}
@Override
diff --git a/src/main/java/seedu/flashlingo/logic/commands/EditCommand.java b/src/main/java/seedu/flashlingo/logic/commands/EditCommand.java
new file mode 100644
index 00000000000..5356e5e19c8
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/EditCommand.java
@@ -0,0 +1,100 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.flashlingo.logic.Messages.MESSAGE_DUPLICATE_FLASHCARD;
+import static seedu.flashlingo.logic.Messages.MESSAGE_INVALID_FLASHCARD_DISPLAYED_INDEX;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_ORIGINAL_WORD;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_ORIGINAL_WORD_LANGUAGE;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_TRANSLATED_WORD;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_TRANSLATED_WORD_LANGUAGE;
+import static seedu.flashlingo.model.Model.PREDICATE_SHOW_ALL_FLASHCARDS;
+
+import java.util.Arrays;
+import java.util.List;
+
+import seedu.flashlingo.commons.core.index.Index;
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.Messages;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.flashcard.FlashCard;
+
+/**
+ * Edits the details of an existing flash card in Flashlingo.
+ */
+public class EditCommand extends Command {
+ public static final String COMMAND_WORD = "edit";
+ public static final String MESSAGE_EDIT_FLASHCARD_SUCCESS = "Edited Flashcard: %1$s";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the flashcard identified "
+ + "by the index number used in the displayed flashcard list. "
+ + "Existing values will be overwritten by the input values.\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + "[" + PREFIX_ORIGINAL_WORD + "WORD] "
+ + "[" + PREFIX_TRANSLATED_WORD + "TRANSLATION] "
+ + "[" + PREFIX_ORIGINAL_WORD_LANGUAGE + "WORD_LANGUAGE] "
+ + "[" + PREFIX_TRANSLATED_WORD_LANGUAGE + "TRANSLATION_LANGUAGE] \n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_ORIGINAL_WORD + "bye "
+ + PREFIX_TRANSLATED_WORD + "再见";
+
+ private final Index index;
+ private final String[] changes;
+ /**
+ * @param index of the flashcard in the list to edit
+ * @param changes details to edit the flashcard with
+ */
+ public EditCommand(Index index, String[] changes) {
+ requireNonNull(index);
+ this.index = index;
+ this.changes = changes;
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredFlashCardList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(MESSAGE_INVALID_FLASHCARD_DISPLAYED_INDEX);
+ }
+
+ FlashCard flashCardToEdit = lastShownList.get(index.getZeroBased());
+ FlashCard editedFlashCard;
+ try {
+ editedFlashCard = flashCardToEdit.editFlashCard(changes);
+ } catch (IllegalArgumentException e) {
+ throw new CommandException(e.getMessage());
+ }
+
+ if ((changes.length > 0 && model.hasFlashCard(editedFlashCard)) || flashCardToEdit.equals(editedFlashCard)) {
+ throw new CommandException(MESSAGE_DUPLICATE_FLASHCARD);
+ }
+ model.setFlashCard(flashCardToEdit, editedFlashCard);
+ model.updateFilteredFlashCardList(PREDICATE_SHOW_ALL_FLASHCARDS);
+ return new CommandResult(String.format(MESSAGE_EDIT_FLASHCARD_SUCCESS, Messages.format(editedFlashCard)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditCommand)) {
+ return false;
+ }
+
+ EditCommand otherEditCommand = (EditCommand) other;
+ return index.equals(otherEditCommand.index)
+ && changes.equals(otherEditCommand.changes);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", index)
+ .add("changes", Arrays.toString(changes))
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/EndCommand.java b/src/main/java/seedu/flashlingo/logic/commands/EndCommand.java
new file mode 100644
index 00000000000..b85b6d5bf2a
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/EndCommand.java
@@ -0,0 +1,46 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.model.Model;
+
+/**
+ * Ends the session of reviewing.
+ */
+public class EndCommand extends Command {
+ public static final String COMMAND_WORD = "end";
+ // For help function
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Ends reviewing session.\n"
+ + "Example: " + COMMAND_WORD + " ";
+
+ public static final String MESSAGE_SUCCESS = "Review Session has ended.";
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.endSession();
+ return new CommandResult(String.format(MESSAGE_SUCCESS));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EndCommand)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("start", "")
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/flashlingo/logic/commands/ExitCommand.java
similarity index 71%
rename from src/main/java/seedu/address/logic/commands/ExitCommand.java
rename to src/main/java/seedu/flashlingo/logic/commands/ExitCommand.java
index 3dd85a8ba90..1fb11603a85 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/flashlingo/logic/commands/ExitCommand.java
@@ -1,19 +1,17 @@
-package seedu.address.logic.commands;
+package seedu.flashlingo.logic.commands;
+
+import seedu.flashlingo.model.Model;
-import seedu.address.model.Model;
/**
* Terminates the program.
*/
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 Flashlingo as requested ...";
@Override
public CommandResult execute(Model model) {
- return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, false);
}
-
}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/flashlingo/logic/commands/FindCommand.java
similarity index 50%
rename from src/main/java/seedu/address/logic/commands/FindCommand.java
rename to src/main/java/seedu/flashlingo/logic/commands/FindCommand.java
index 72b9eddd3a7..e1e699fd959 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/flashlingo/logic/commands/FindCommand.java
@@ -1,37 +1,39 @@
-package seedu.address.logic.commands;
+package seedu.flashlingo.logic.commands;
import static java.util.Objects.requireNonNull;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.Messages;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.flashcard.WordContainsKeywordsPredicate;
/**
- * Finds and lists all persons in address book whose name contains any of the argument keywords.
- * Keyword matching is case insensitive.
+ * Finds and lists all flash cards in flashlingo whose original words contains any of the argument keywords.
*/
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 "
- + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n"
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all flashcards whose words contain any of "
+ + "the specified keywords and displays them as a list with index numbers.\n"
+ "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
- + "Example: " + COMMAND_WORD + " alice bob charlie";
+ + "Example: " + COMMAND_WORD + " shark";
- private final NameContainsKeywordsPredicate predicate;
+ /**
+ * The predicate used to filter the flash card list with specified keywords.
+ */
+ private final WordContainsKeywordsPredicate predicate;
- public FindCommand(NameContainsKeywordsPredicate predicate) {
+ public FindCommand(WordContainsKeywordsPredicate predicate) {
this.predicate = predicate;
}
-
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.updateFilteredPersonList(predicate);
+ model.updateFilteredFlashCardList(predicate);
return new CommandResult(
- String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
+ String.format(Messages.MESSAGE_FLASHCARDS_LISTED_OVERVIEW,
+ model.getFilteredFlashCardList().size()));
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/flashlingo/logic/commands/HelpCommand.java
similarity index 85%
rename from src/main/java/seedu/address/logic/commands/HelpCommand.java
rename to src/main/java/seedu/flashlingo/logic/commands/HelpCommand.java
index bf824f91bd0..14cbfceaeaa 100644
--- a/src/main/java/seedu/address/logic/commands/HelpCommand.java
+++ b/src/main/java/seedu/flashlingo/logic/commands/HelpCommand.java
@@ -1,6 +1,6 @@
-package seedu.address.logic.commands;
+package seedu.flashlingo.logic.commands;
-import seedu.address.model.Model;
+import seedu.flashlingo.model.Model;
/**
* Format full help instructions for every command for display.
@@ -16,6 +16,6 @@ public class HelpCommand extends Command {
@Override
public CommandResult execute(Model model) {
- return new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ return new CommandResult(SHOWING_HELP_MESSAGE, true, false, false);
}
}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/LanguageCommand.java b/src/main/java/seedu/flashlingo/logic/commands/LanguageCommand.java
new file mode 100644
index 00000000000..3cb7d99761a
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/LanguageCommand.java
@@ -0,0 +1,60 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.Messages;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.flashcard.WordLanguagePredicate;
+
+/**
+ * Finds and lists all flash cards in flashlingo whose original words or translations are in a certain language.
+ */
+public class LanguageCommand extends Command {
+
+ public static final String COMMAND_WORD = "language";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all flashcards whose language is "
+ + "the specified language and displays them as a list with index numbers.\n"
+ + "Parameters: LANGUAGE\n"
+ + "Example: " + COMMAND_WORD + " English";
+
+ /**
+ * The predicate used to filter the flash card list with specified language.
+ */
+ private final WordLanguagePredicate predicate;
+
+ public LanguageCommand(WordLanguagePredicate predicate) {
+ this.predicate = predicate;
+ }
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredFlashCardList(predicate);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_FLASHCARDS_LISTED_OVERVIEW,
+ model.getFilteredFlashCardList().size()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof LanguageCommand)) {
+ return false;
+ }
+
+ LanguageCommand otherLanguageCommand = (LanguageCommand) other;
+ return predicate.equals(otherLanguageCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/ListCommand.java b/src/main/java/seedu/flashlingo/logic/commands/ListCommand.java
new file mode 100644
index 00000000000..5b8ea99a856
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/ListCommand.java
@@ -0,0 +1,24 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.flashlingo.model.Model.PREDICATE_SHOW_ALL_FLASHCARDS;
+
+import seedu.flashlingo.model.Model;
+
+/**
+ * Lists all flash cards in Flashlingo to the user.
+ */
+public class ListCommand extends Command {
+
+ public static final String COMMAND_WORD = "list";
+
+ public static final String MESSAGE_SUCCESS = "Listed all flashcards";
+
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredFlashCardList(PREDICATE_SHOW_ALL_FLASHCARDS);
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/LoadCommand.java b/src/main/java/seedu/flashlingo/logic/commands/LoadCommand.java
new file mode 100644
index 00000000000..b0b2a80e1f6
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/LoadCommand.java
@@ -0,0 +1,131 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.flashlingo.logic.Messages.MESSAGE_OPEN_FILE_FAIL;
+import static seedu.flashlingo.logic.Messages.MESSAGE_READ_FILE_FAIL;
+
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.Date;
+
+import org.apache.poi.ss.usermodel.DataFormatter;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.flashcard.FlashCard;
+import seedu.flashlingo.model.flashcard.ProficiencyLevel;
+import seedu.flashlingo.model.flashcard.words.OriginalWord;
+import seedu.flashlingo.model.flashcard.words.TranslatedWord;
+
+/**
+ * Loads a xlsx file to add flash cards to Flashlingo.
+ */
+public class LoadCommand extends Command {
+ public static final String COMMAND_WORD = "load";
+ public static final String MESSAGE_SUCCESS = "You have successfully loaded file: ";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Loads the xlsx file from specified path.\n"
+ + "Example: " + COMMAND_WORD + " words.xlsx";
+ public static final String MESSAGE_DUPLICATE_FLASHCARD = " flash card already exists!";
+ public static final String MESSAGE_EMPTY_WORDS = "Word/translation cannot be empty!";
+ public final String fileName;
+
+ public LoadCommand(String fileName) {
+ this.fileName = fileName;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ // Tries to access and open the file.
+ FileInputStream fis;
+ try {
+ fis = new FileInputStream(fileName);
+ } catch (Exception e) {
+ throw new CommandException(MESSAGE_OPEN_FILE_FAIL);
+ }
+
+ // Tries to read the file.
+ ArrayList flashCards;
+ try {
+ flashCards = readFromExcel(fis);
+ } catch (CommandException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new CommandException(MESSAGE_READ_FILE_FAIL);
+ }
+
+ addFlashCardsToModel(flashCards, model);
+
+ return new CommandResult(MESSAGE_SUCCESS + fileName, false, false, false);
+ }
+
+ private ArrayList readFromExcel(FileInputStream fis) throws Exception {
+ ArrayList flashCards = new ArrayList<>();
+
+ Workbook workbook = new XSSFWorkbook(fis);
+ Sheet sheet = workbook.getSheetAt(0);
+ DataFormatter formatter = new DataFormatter();
+ for (Row row : sheet) {
+ String originalWordValue = formatter.formatCellValue(row.getCell(0));
+ String translatedWordValue = formatter.formatCellValue(row.getCell(1));
+ ArrayList validWords = trimAndVerifyWords(originalWordValue, translatedWordValue);
+ String originalWord = validWords.get(0);
+ String translatedWord = validWords.get(1);
+ FlashCard flashCard = new FlashCard(new OriginalWord(originalWord, ""),
+ new TranslatedWord(translatedWord, ""), new Date(), new ProficiencyLevel(1));
+ flashCards.add(flashCard);
+ }
+
+ return flashCards;
+ }
+
+ private ArrayList trimAndVerifyWords(String originalWordValue, String translatedWordValue)
+ throws CommandException {
+ ArrayList validWords = new ArrayList<>();
+ originalWordValue = originalWordValue.trim();
+ translatedWordValue = translatedWordValue.trim();
+ if (originalWordValue.equals("") || translatedWordValue.equals("")) {
+ throw new CommandException(MESSAGE_EMPTY_WORDS);
+ }
+ validWords.add(originalWordValue);
+ validWords.add(translatedWordValue);
+ return validWords;
+ }
+
+ private void addFlashCardsToModel(ArrayList flashCards, Model model) throws CommandException {
+ // Checks for duplicated flash cards with the app.
+ for (FlashCard flashCard : flashCards) {
+ if (model.hasFlashCard(flashCard)) {
+ throw new CommandException(flashCard.getOriginalWord().getWord() + "-"
+ + flashCard.getTranslatedWord().getWord() + MESSAGE_DUPLICATE_FLASHCARD);
+ }
+ }
+ model.addFlashCards(flashCards);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof LoadCommand)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/NoCommand.java b/src/main/java/seedu/flashlingo/logic/commands/NoCommand.java
new file mode 100644
index 00000000000..856fcc55839
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/NoCommand.java
@@ -0,0 +1,66 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.flashlingo.model.Model.PREDICATE_SHOW_ALL_FLASHCARDS;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.logic.session.SessionManager;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.flashcard.FlashCard;
+
+/**
+ * Indicates user has not yet memorized the word.
+ */
+public class NoCommand extends Command {
+
+ public static final String COMMAND_WORD = "no";
+
+ // For help function
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Indicates user hasn't memorized the word.\n"
+ + "Example: " + COMMAND_WORD + " ";
+
+ public static final String MESSAGE_SUCCESS = "It seems like that you did not memorize this word well.\n";
+
+
+ /**
+ * Creates an NoCommand.
+ */
+ public NoCommand() {};
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ FlashCard response = model.nextReviewWord();
+ response.updateLevel(false);
+ response.forgetFlashCard();
+ // Deals with the case where there's no more words to review
+ if (!model.hasNextRound()) {
+ SessionManager.getInstance().setSession(false);
+ model.updateFilteredFlashCardList(PREDICATE_SHOW_ALL_FLASHCARDS);
+ return new CommandResult(MESSAGE_SUCCESS + "\nYou have no more words to review!");
+ }
+ // Deals with the case where there's more words to review
+ model.nextReviewWord();
+ return new CommandResult(MESSAGE_SUCCESS + "\nThe next word is: ");
+ }
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof NoCommand)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/RevealCommand.java b/src/main/java/seedu/flashlingo/logic/commands/RevealCommand.java
new file mode 100644
index 00000000000..694384ec8ea
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/RevealCommand.java
@@ -0,0 +1,71 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.flashlingo.commons.core.index.Index;
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.Messages;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.flashcard.FlashCard;
+import seedu.flashlingo.model.flashcard.words.TranslatedWord;
+
+/**
+ * Reveals the other side of the flash card.
+ */
+public class RevealCommand extends Command {
+ public static final String COMMAND_WORD = "reveal";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Reveals the flashcard identified by the index number "
+ + "used\nParameters: [] \n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_SUCCESS = "The translation is: ";
+ private final Index targetIndex;
+ /**
+ * Creates an RevealCommand.
+ */
+ public RevealCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredFlashCardList();
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_FLASHCARD_DISPLAYED_INDEX);
+ }
+
+ FlashCard toBeRevealed = lastShownList.get(targetIndex.getZeroBased());
+ TranslatedWord translatedWord = model.reveal(toBeRevealed);
+
+ if (!toBeRevealed.getIsRevealed()) { // If already revealed -> Hide
+ return new CommandResult("Translation has been hidden");
+ }
+ return new CommandResult(MESSAGE_SUCCESS + translatedWord.getWord());
+
+ }
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof RevealCommand)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/ReviewCommand.java b/src/main/java/seedu/flashlingo/logic/commands/ReviewCommand.java
new file mode 100644
index 00000000000..69de6cb72ce
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/ReviewCommand.java
@@ -0,0 +1,48 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.flashlingo.logic.Messages;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.flashcard.WordOverduePredicate;
+
+/**
+ * Finds and lists all flash cards in flashlingo who is overdue.
+ */
+public class ReviewCommand extends Command {
+
+ public static final String COMMAND_WORD = "review";
+
+ public static final String MESSAGE_SUCCESS = "Listed all flashcards you need to review";
+
+ /**
+ * The predicate used to filter the flash card which is overdue.
+ */
+ private final WordOverduePredicate predicate;
+
+ public ReviewCommand() {
+ this.predicate = new WordOverduePredicate();
+ }
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredFlashCardList(predicate);
+ return new CommandResult(MESSAGE_SUCCESS + "\n"
+ + String.format(Messages.MESSAGE_FLASHCARDS_LISTED_OVERVIEW,
+ model.getFilteredFlashCardList().size()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ReviewCommand)) {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/StartCommand.java b/src/main/java/seedu/flashlingo/logic/commands/StartCommand.java
new file mode 100644
index 00000000000..954b9a6216b
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/StartCommand.java
@@ -0,0 +1,49 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.model.Model;
+
+
+/**
+ * Starts a new session of reviewing.
+ */
+public class StartCommand extends Command {
+
+ public static final String COMMAND_WORD = "start";
+
+ // For help function
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Starts reviewing session.\n"
+ + "Example: " + COMMAND_WORD + " ";
+
+ public static final String MESSAGE_SUCCESS = "Review Session has been started.";
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.startSession();
+ return new CommandResult(String.format(MESSAGE_SUCCESS));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof StartCommand)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("start", "")
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/StatsCommand.java b/src/main/java/seedu/flashlingo/logic/commands/StatsCommand.java
new file mode 100644
index 00000000000..ec1be3f5599
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/StatsCommand.java
@@ -0,0 +1,61 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.flashlingo.model.Model;
+
+/**
+ * Indicates user wants to display the learning statistics
+ */
+public class StatsCommand extends Command {
+ /** Command word to display usage statistics **/
+ public static final String COMMAND_WORD = "stats";
+
+ // For stats function
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Show user statistics\n"
+ + "Example: " + COMMAND_WORD + " ";
+
+ public static final String MESSAGE_SUCCESS = "Great work fellow learner! \nTotal number of flash cards: %d \n"
+ + "Total number of flash cards remembered: %d\nOverall success rate: %f%%";
+
+ /**
+ * Executes the functioning for this command
+ * @param model {@code Model} which the command should operate on.
+ * @return CommandResult after executing this command
+ */
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ int numberOfFlashCards = model.getNumberOfFlashCards();
+ double numberOfRememberedWords = model.getNumberOfRememberedWords();
+ double successRate = 0;
+ boolean isNonNegativeNumberOfWords = numberOfRememberedWords < 0;
+ boolean isNonZeroNonNegativeNumberOfFlashCards = numberOfFlashCards <= 0;
+ if (!(isNonNegativeNumberOfWords || isNonZeroNonNegativeNumberOfFlashCards)) {
+ successRate = (numberOfRememberedWords / numberOfFlashCards) * 100;
+ }
+
+ return new CommandResult(String.format(MESSAGE_SUCCESS, numberOfFlashCards,
+ (int) numberOfRememberedWords, successRate));
+ }
+
+ /**
+ * Checks whether this is equal to the passed Object
+ * @param other The passed object to check for equality against
+ * @return True or False depending on whether this and other are equal
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof StatsCommand)) {
+ return false;
+ }
+
+ return true;
+ }
+
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/SwitchCommand.java b/src/main/java/seedu/flashlingo/logic/commands/SwitchCommand.java
new file mode 100644
index 00000000000..f1cc1d5eb43
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/SwitchCommand.java
@@ -0,0 +1,45 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.model.Model;
+
+/**
+ * Switches the color theme of the application.
+ */
+public class SwitchCommand extends Command {
+ public static final String COMMAND_WORD = "switch";
+ public static final String MESSAGE_SUCCESS = "You have switched to ";
+
+ public SwitchCommand() {}
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.switchTheme();
+ String currentTheme = model.getTheme().equals("Default") ? "light" : "dark";
+ return new CommandResult(MESSAGE_SUCCESS + currentTheme + " theme!", false, false, true);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof SwitchCommand)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/commands/YesCommand.java b/src/main/java/seedu/flashlingo/logic/commands/YesCommand.java
new file mode 100644
index 00000000000..bbb8319b170
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/commands/YesCommand.java
@@ -0,0 +1,66 @@
+package seedu.flashlingo.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.flashlingo.model.Model.PREDICATE_SHOW_ALL_FLASHCARDS;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.logic.session.SessionManager;
+import seedu.flashlingo.model.Model;
+import seedu.flashlingo.model.flashcard.FlashCard;
+
+
+/**
+ * Indicates user has memorized the word.
+ */
+public class YesCommand extends Command {
+
+ public static final String COMMAND_WORD = "yes";
+
+ // For help function
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Indicates user has successfully memorized the word.\n"
+ + "Example: " + COMMAND_WORD + " ";
+
+ public static final String MESSAGE_SUCCESS = "Great Job! You have indicated that you have memorized the word!\n";
+ /**
+ * Creates an YesCommand.
+ */
+ public YesCommand() {}
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ FlashCard response = model.nextReviewWord();
+ response.updateLevel(true);
+ response.recallFlashCard();
+ // Deals with the case where there's no more words to review
+ if (!model.hasNextRound()) {
+ SessionManager.getInstance().setSession(false);
+ model.updateFilteredFlashCardList(PREDICATE_SHOW_ALL_FLASHCARDS);
+ return new CommandResult(MESSAGE_SUCCESS + "\nYou have no more words to review!");
+ }
+ // Deals with the case where there's more words to review
+ model.nextReviewWord();
+ return new CommandResult(MESSAGE_SUCCESS + "\nThe next word is: ");
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof YesCommand)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/flashlingo/logic/commands/exceptions/CommandException.java
similarity index 81%
rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
rename to src/main/java/seedu/flashlingo/logic/commands/exceptions/CommandException.java
index a16bd14f2cd..255a5571cc3 100644
--- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
+++ b/src/main/java/seedu/flashlingo/logic/commands/exceptions/CommandException.java
@@ -1,7 +1,7 @@
-package seedu.address.logic.commands.exceptions;
+package seedu.flashlingo.logic.commands.exceptions;
/**
- * Represents an error which occurs during execution of a {@link Command}.
+ * Represents an error which occurs during execution of a {@link seedu.flashlingo.logic.commands.Command}.
*/
public class CommandException extends Exception {
public CommandException(String message) {
diff --git a/src/main/java/seedu/flashlingo/logic/parser/AddCommandParser.java b/src/main/java/seedu/flashlingo/logic/parser/AddCommandParser.java
new file mode 100644
index 00000000000..e21ffdb6b1c
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/parser/AddCommandParser.java
@@ -0,0 +1,75 @@
+package seedu.flashlingo.logic.parser;
+
+import static seedu.flashlingo.logic.Messages.MESSAGE_EMPTY_VALUE;
+import static seedu.flashlingo.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_ORIGINAL_WORD;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_ORIGINAL_WORD_LANGUAGE;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_TRANSLATED_WORD;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_TRANSLATED_WORD_LANGUAGE;
+
+import java.util.stream.Stream;
+
+import seedu.flashlingo.logic.commands.AddCommand;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.model.flashcard.words.OriginalWord;
+import seedu.flashlingo.model.flashcard.words.TranslatedWord;
+
+/**
+ * Parses input arguments and creates a new AddCommand object.
+ */
+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 {
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_ORIGINAL_WORD, PREFIX_ORIGINAL_WORD_LANGUAGE,
+ PREFIX_TRANSLATED_WORD, PREFIX_TRANSLATED_WORD_LANGUAGE);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_ORIGINAL_WORD, PREFIX_TRANSLATED_WORD)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_ORIGINAL_WORD, PREFIX_ORIGINAL_WORD_LANGUAGE,
+ PREFIX_TRANSLATED_WORD, PREFIX_TRANSLATED_WORD_LANGUAGE);
+ return new AddCommand(getOriginalWord(argMultimap), getTranslationWord(argMultimap));
+ }
+
+ private OriginalWord getOriginalWord(ArgumentMultimap argMultimap) throws ParseException {
+ String originalWord = argMultimap.getValue(PREFIX_ORIGINAL_WORD).get().trim();
+ if (originalWord.isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_EMPTY_VALUE, AddCommand.MESSAGE_USAGE));
+ }
+ if (arePrefixesPresent(argMultimap, PREFIX_ORIGINAL_WORD_LANGUAGE)) {
+ String language = argMultimap.getValue(PREFIX_ORIGINAL_WORD_LANGUAGE).get();
+ return new OriginalWord(originalWord, language);
+ } else {
+ return new OriginalWord(originalWord);
+ }
+ }
+
+ private TranslatedWord getTranslationWord(ArgumentMultimap argMultimap) throws ParseException {
+ String translationWord = argMultimap.getValue(PREFIX_TRANSLATED_WORD).get().trim();
+ if (translationWord.isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_EMPTY_VALUE, AddCommand.MESSAGE_USAGE));
+ }
+ if (arePrefixesPresent(argMultimap, PREFIX_TRANSLATED_WORD_LANGUAGE)) {
+ String language = argMultimap.getValue(PREFIX_TRANSLATED_WORD_LANGUAGE).get();
+ return new TranslatedWord(translationWord, language);
+ } else {
+ return new TranslatedWord(translationWord);
+ }
+ }
+
+ //@@author
+ /**
+ * 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());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/flashlingo/logic/parser/ArgumentMultimap.java
similarity index 95%
rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
rename to src/main/java/seedu/flashlingo/logic/parser/ArgumentMultimap.java
index 21e26887a83..7c8401e0f6b 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
+++ b/src/main/java/seedu/flashlingo/logic/parser/ArgumentMultimap.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.parser;
+package seedu.flashlingo.logic.parser;
import java.util.ArrayList;
import java.util.HashMap;
@@ -7,8 +7,8 @@
import java.util.Optional;
import java.util.stream.Stream;
-import seedu.address.logic.Messages;
-import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.logic.Messages;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
/**
* Stores mapping of prefixes to their respective arguments.
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/flashlingo/logic/parser/ArgumentTokenizer.java
similarity index 99%
rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
rename to src/main/java/seedu/flashlingo/logic/parser/ArgumentTokenizer.java
index 5c9aebfa488..184637a8580 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
+++ b/src/main/java/seedu/flashlingo/logic/parser/ArgumentTokenizer.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.parser;
+package seedu.flashlingo.logic.parser;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/src/main/java/seedu/flashlingo/logic/parser/CliSyntax.java b/src/main/java/seedu/flashlingo/logic/parser/CliSyntax.java
new file mode 100644
index 00000000000..6dbbe9ee966
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/parser/CliSyntax.java
@@ -0,0 +1,17 @@
+//@@author A1WAYSD
+//Inspired from AB-3's manipulations on prefix
+package seedu.flashlingo.logic.parser;
+
+/**
+ * Contains Command Line Interface (CLI) syntax definitions common to multiple commands.
+ */
+public class CliSyntax {
+
+ /* Prefix definitions */
+ public static final Prefix PREFIX_ORIGINAL_WORD = new Prefix("w/");
+ public static final Prefix PREFIX_ORIGINAL_WORD_LANGUAGE = new Prefix("wl/");
+ public static final Prefix PREFIX_TRANSLATED_WORD = new Prefix("t/");
+ public static final Prefix PREFIX_TRANSLATED_WORD_LANGUAGE = new Prefix("tl/");
+
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/flashlingo/logic/parser/DeleteCommandParser.java
similarity index 72%
rename from src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
rename to src/main/java/seedu/flashlingo/logic/parser/DeleteCommandParser.java
index 3527fe76a3e..68a819547ac 100644
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ b/src/main/java/seedu/flashlingo/logic/parser/DeleteCommandParser.java
@@ -1,10 +1,10 @@
-package seedu.address.logic.parser;
+package seedu.flashlingo.logic.parser;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.flashlingo.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.DeleteCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.commons.core.index.Index;
+import seedu.flashlingo.logic.commands.DeleteCommand;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
/**
* Parses input arguments and creates a new DeleteCommand object
@@ -25,5 +25,4 @@ public DeleteCommand parse(String args) throws ParseException {
String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe);
}
}
-
}
diff --git a/src/main/java/seedu/flashlingo/logic/parser/EditCommandParser.java b/src/main/java/seedu/flashlingo/logic/parser/EditCommandParser.java
new file mode 100644
index 00000000000..ad09c29a4f9
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/parser/EditCommandParser.java
@@ -0,0 +1,67 @@
+package seedu.flashlingo.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.flashlingo.logic.Messages.MESSAGE_EMPTY_VALUE;
+import static seedu.flashlingo.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_ORIGINAL_WORD;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_ORIGINAL_WORD_LANGUAGE;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_TRANSLATED_WORD;
+import static seedu.flashlingo.logic.parser.CliSyntax.PREFIX_TRANSLATED_WORD_LANGUAGE;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+import seedu.flashlingo.commons.core.index.Index;
+import seedu.flashlingo.logic.commands.EditCommand;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new EditCommand object
+ */
+public class EditCommandParser implements Parser {
+ private final Prefix[] prefixes = new Prefix[] {
+ PREFIX_ORIGINAL_WORD, PREFIX_ORIGINAL_WORD_LANGUAGE, PREFIX_TRANSLATED_WORD, PREFIX_TRANSLATED_WORD_LANGUAGE};
+
+ /**
+ * 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, prefixes);
+
+ Index index;
+
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_ORIGINAL_WORD, PREFIX_ORIGINAL_WORD_LANGUAGE,
+ PREFIX_TRANSLATED_WORD, PREFIX_TRANSLATED_WORD_LANGUAGE);
+
+ String[] changes = getChanges(argMultimap);
+ if (Arrays.equals(changes, new String[4])) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE));
+ }
+
+ return new EditCommand(index, changes);
+ }
+ private String[] getChanges(ArgumentMultimap argMultimap) throws ParseException {
+ String[] changes = new String[4];
+ for (int i = 0; i < prefixes.length; i++) {
+ Optional temp = argMultimap.getValue(prefixes[i]);
+ changes[i] = temp.orElse(null);
+ }
+ if (isInvalidWord(changes[0]) || isInvalidWord(changes[2])) {
+ throw new ParseException(MESSAGE_EMPTY_VALUE);
+ }
+ return changes;
+ }
+ private boolean isInvalidWord(String change) {
+ return change != null && change.isEmpty();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/parser/FindCommandParser.java b/src/main/java/seedu/flashlingo/logic/parser/FindCommandParser.java
new file mode 100644
index 00000000000..7467346e2da
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/parser/FindCommandParser.java
@@ -0,0 +1,49 @@
+package seedu.flashlingo.logic.parser;
+
+import static seedu.flashlingo.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import java.util.Arrays;
+
+import seedu.flashlingo.logic.commands.FindCommand;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.model.flashcard.WordContainsKeywordsPredicate;
+
+
+/**
+ * Parses input arguments and creates a new FindCommand object
+ */
+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 {
+ String trimmedArgs = parseWord(args);
+
+ String[] nameKeywords = trimmedArgs.split(",");
+
+ if (nameKeywords.length == 0) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ }
+
+ for (int i = 0; i < nameKeywords.length; i++) {
+ nameKeywords[i] = parseWord(nameKeywords[i]);
+ }
+
+ System.out.println(Arrays.toString(nameKeywords));
+
+ return new FindCommand(new WordContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
+ }
+
+ private String parseWord(String word) throws ParseException {
+ if (word.trim().isBlank()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ }
+ return word.trim();
+ }
+
+}
diff --git a/src/main/java/seedu/flashlingo/logic/parser/FlashlingoParser.java b/src/main/java/seedu/flashlingo/logic/parser/FlashlingoParser.java
new file mode 100644
index 00000000000..ac7b4ae02c8
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/parser/FlashlingoParser.java
@@ -0,0 +1,128 @@
+package seedu.flashlingo.logic.parser;
+
+import static seedu.flashlingo.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.flashlingo.logic.Messages.MESSAGE_IN_REVIEW_SESSION;
+import static seedu.flashlingo.logic.Messages.MESSAGE_NOT_IN_REVIEW_SESSION;
+import static seedu.flashlingo.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
+
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.logic.commands.AddCommand;
+import seedu.flashlingo.logic.commands.Command;
+import seedu.flashlingo.logic.commands.DeleteCommand;
+import seedu.flashlingo.logic.commands.EditCommand;
+import seedu.flashlingo.logic.commands.EndCommand;
+import seedu.flashlingo.logic.commands.ExitCommand;
+import seedu.flashlingo.logic.commands.FindCommand;
+import seedu.flashlingo.logic.commands.HelpCommand;
+import seedu.flashlingo.logic.commands.LanguageCommand;
+import seedu.flashlingo.logic.commands.ListCommand;
+import seedu.flashlingo.logic.commands.LoadCommand;
+import seedu.flashlingo.logic.commands.NoCommand;
+import seedu.flashlingo.logic.commands.RevealCommand;
+import seedu.flashlingo.logic.commands.ReviewCommand;
+import seedu.flashlingo.logic.commands.StartCommand;
+import seedu.flashlingo.logic.commands.StatsCommand;
+import seedu.flashlingo.logic.commands.SwitchCommand;
+import seedu.flashlingo.logic.commands.YesCommand;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.logic.session.SessionManager;
+
+/**
+ * Parses user input.
+ */
+public class FlashlingoParser {
+ //@@author
+ /**
+ * Used for initial separation of command word and args.
+ */
+ private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)");
+ private static final Logger logger = LogsCenter.getLogger(FlashlingoParser.class);
+ private static final SessionManager sessionManager = SessionManager.getInstance();
+ /**
+ * Parses user input into command for execution.
+ *
+ * @param userInput full user input string
+ * @return the command based on the user input
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ 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));
+ }
+
+ final String commandWord = matcher.group("commandWord");
+ final String arguments = matcher.group("arguments");
+
+ // Note to developers: Change the log level in config.json to enable lower level (i.e., FINE, FINER and lower)
+ // log messages such as the one below.
+ // Lower level log messages are used sparingly to minimize noise in the code.
+ logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);
+ if (sessionManager.isReviewSession()) {
+ switch (commandWord) {
+ case StartCommand.COMMAND_WORD:
+ throw new ParseException(MESSAGE_IN_REVIEW_SESSION);
+ case EndCommand.COMMAND_WORD:
+ return new EndCommand();
+ case YesCommand.COMMAND_WORD:
+ return new YesCommand();
+ case NoCommand.COMMAND_WORD:
+ return new NoCommand();
+ case ExitCommand.COMMAND_WORD:
+ return new ExitCommand();
+ case RevealCommand.COMMAND_WORD:
+ return new RevealCommandParser().parse(arguments);
+ case SwitchCommand.COMMAND_WORD:
+ return new SwitchCommand();
+ case HelpCommand.COMMAND_WORD:
+ return new HelpCommand();
+ default:
+ logger.finer("This user input caused a ParseException: " + userInput);
+ throw new ParseException(MESSAGE_IN_REVIEW_SESSION);
+ }
+ }
+ switch (commandWord) {
+ case AddCommand.COMMAND_WORD:
+ return new AddCommandParser().parse(arguments);
+ case DeleteCommand.COMMAND_WORD:
+ return new DeleteCommandParser().parse(arguments);
+ case EditCommand.COMMAND_WORD:
+ return new EditCommandParser().parse(arguments);
+ case FindCommand.COMMAND_WORD:
+ return new FindCommandParser().parse(arguments);
+ case NoCommand.COMMAND_WORD:
+ throw new ParseException(MESSAGE_NOT_IN_REVIEW_SESSION);
+ case LanguageCommand.COMMAND_WORD:
+ return new LanguageCommandParser().parse(arguments);
+ case YesCommand.COMMAND_WORD:
+ throw new ParseException(MESSAGE_NOT_IN_REVIEW_SESSION);
+ case ExitCommand.COMMAND_WORD:
+ return new ExitCommand();
+ case HelpCommand.COMMAND_WORD:
+ return new HelpCommand();
+ case ListCommand.COMMAND_WORD:
+ return new ListCommand();
+ case ReviewCommand.COMMAND_WORD:
+ return new ReviewCommand();
+ case StatsCommand.COMMAND_WORD:
+ return new StatsCommand();
+ case StartCommand.COMMAND_WORD:
+ return new StartCommand();
+ case EndCommand.COMMAND_WORD:
+ throw new ParseException(MESSAGE_NOT_IN_REVIEW_SESSION);
+ case SwitchCommand.COMMAND_WORD:
+ return new SwitchCommand();
+ case RevealCommand.COMMAND_WORD:
+ return new RevealCommandParser().parse(arguments);
+ case LoadCommand.COMMAND_WORD:
+ return new LoadCommandParser().parse(arguments);
+ default:
+ logger.finer("This user input caused a ParseException: " + userInput);
+ throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
+ }
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/logic/parser/LanguageCommandParser.java b/src/main/java/seedu/flashlingo/logic/parser/LanguageCommandParser.java
new file mode 100644
index 00000000000..767469b62a4
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/parser/LanguageCommandParser.java
@@ -0,0 +1,21 @@
+package seedu.flashlingo.logic.parser;
+
+import seedu.flashlingo.logic.commands.LanguageCommand;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.model.flashcard.WordLanguagePredicate;
+
+/**
+ * Parses input arguments and creates a new LanguageCommand object
+ */
+public class LanguageCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the LanguageCommand
+ * and returns a LanguageCommand object for execution.
+ */
+ public LanguageCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ return new LanguageCommand(new WordLanguagePredicate(trimmedArgs));
+ }
+
+}
diff --git a/src/main/java/seedu/flashlingo/logic/parser/LoadCommandParser.java b/src/main/java/seedu/flashlingo/logic/parser/LoadCommandParser.java
new file mode 100644
index 00000000000..893edd76c17
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/parser/LoadCommandParser.java
@@ -0,0 +1,22 @@
+package seedu.flashlingo.logic.parser;
+
+import static seedu.flashlingo.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.flashlingo.logic.commands.LoadCommand;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input argument, the file path, and creates a new LoadCommand object.
+ */
+public class LoadCommandParser implements Parser {
+
+ @Override
+ public LoadCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, LoadCommand.MESSAGE_USAGE));
+ }
+ return new LoadCommand(trimmedArgs);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/flashlingo/logic/parser/Parser.java
similarity index 71%
rename from src/main/java/seedu/address/logic/parser/Parser.java
rename to src/main/java/seedu/flashlingo/logic/parser/Parser.java
index d6551ad8e3f..c6caffbeea6 100644
--- a/src/main/java/seedu/address/logic/parser/Parser.java
+++ b/src/main/java/seedu/flashlingo/logic/parser/Parser.java
@@ -1,7 +1,7 @@
-package seedu.address.logic.parser;
+package seedu.flashlingo.logic.parser;
-import seedu.address.logic.commands.Command;
-import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.logic.commands.Command;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
/**
* Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}.
diff --git a/src/main/java/seedu/flashlingo/logic/parser/ParserUtil.java b/src/main/java/seedu/flashlingo/logic/parser/ParserUtil.java
new file mode 100644
index 00000000000..79f541c63be
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/parser/ParserUtil.java
@@ -0,0 +1,27 @@
+//@@author
+package seedu.flashlingo.logic.parser;
+
+import seedu.flashlingo.commons.core.index.Index;
+import seedu.flashlingo.commons.util.StringUtil;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+
+/**
+ * Contains utility methods used for parsing strings in the various *Parser classes.
+ */
+public class ParserUtil {
+ //@@author
+ 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.
+ * @throws ParseException if the specified index is invalid (not non-zero unsigned integer).
+ */
+ public static Index parseIndex(String oneBasedIndex) throws ParseException {
+ String trimmedIndex = oneBasedIndex.trim();
+ if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
+ throw new ParseException(MESSAGE_INVALID_INDEX);
+ }
+ return Index.fromOneBased(Integer.parseInt(trimmedIndex));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/flashlingo/logic/parser/Prefix.java
similarity index 95%
rename from src/main/java/seedu/address/logic/parser/Prefix.java
rename to src/main/java/seedu/flashlingo/logic/parser/Prefix.java
index 348b7686c8a..fcc1a9133d1 100644
--- a/src/main/java/seedu/address/logic/parser/Prefix.java
+++ b/src/main/java/seedu/flashlingo/logic/parser/Prefix.java
@@ -1,4 +1,4 @@
-package seedu.address.logic.parser;
+package seedu.flashlingo.logic.parser;
/**
* A prefix that marks the beginning of an argument in an arguments string.
diff --git a/src/main/java/seedu/flashlingo/logic/parser/RevealCommandParser.java b/src/main/java/seedu/flashlingo/logic/parser/RevealCommandParser.java
new file mode 100644
index 00000000000..a7ca8b07c49
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/parser/RevealCommandParser.java
@@ -0,0 +1,34 @@
+package seedu.flashlingo.logic.parser;
+
+import static seedu.flashlingo.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.flashlingo.commons.core.index.Index;
+import seedu.flashlingo.logic.commands.RevealCommand;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new RevealCommand object
+ */
+public class RevealCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the RevealCommand
+ * and returns a RevealCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public RevealCommand parse(String args) throws ParseException {
+ try {
+ Index index;
+ if (args.trim().isBlank()) {
+ index = ParserUtil.parseIndex("1");
+ } else {
+ index = ParserUtil.parseIndex(args);
+ }
+ return new RevealCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, RevealCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/flashlingo/logic/parser/exceptions/ParseException.java
similarity index 72%
rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java
rename to src/main/java/seedu/flashlingo/logic/parser/exceptions/ParseException.java
index 158a1a54c1c..758f1417b3a 100644
--- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java
+++ b/src/main/java/seedu/flashlingo/logic/parser/exceptions/ParseException.java
@@ -1,6 +1,6 @@
-package seedu.address.logic.parser.exceptions;
+package seedu.flashlingo.logic.parser.exceptions;
-import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.flashlingo.commons.exceptions.IllegalValueException;
/**
* Represents a parse error encountered by a parser.
diff --git a/src/main/java/seedu/flashlingo/logic/session/SessionManager.java b/src/main/java/seedu/flashlingo/logic/session/SessionManager.java
new file mode 100644
index 00000000000..01472d86107
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/logic/session/SessionManager.java
@@ -0,0 +1,38 @@
+package seedu.flashlingo.logic.session;
+
+/**
+ * The SessionManager class implements the Session interface and is responsible for managing language learning sessions.
+ */
+public class SessionManager {
+ private static SessionManager instance = null;
+ private static boolean isReviewSession = false;
+
+ private SessionManager() {
+ // Private constructor to prevent external instantiation.
+ }
+ /**
+ * Returns the instance of the SessionManager class.
+ * @return The instance of the SessionManager class.
+ */
+ public static SessionManager getInstance() {
+ if (instance == null) {
+ instance = new SessionManager();
+ }
+ return instance;
+ }
+ /**
+ * Sets the session to be a review session or not.
+ * @param editedReviewSession The boolean value to set the session to.
+ */
+ public void setSession(boolean editedReviewSession) {
+ isReviewSession = editedReviewSession;
+ }
+ /**
+ * Returns whether the session is a review session.
+ * @return The boolean value of whether the session is a review session.
+ */
+ public boolean isReviewSession() {
+ return isReviewSession;
+ }
+}
+
diff --git a/src/main/java/seedu/flashlingo/model/Flashlingo.java b/src/main/java/seedu/flashlingo/model/Flashlingo.java
new file mode 100644
index 00000000000..55056d3cf99
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/Flashlingo.java
@@ -0,0 +1,132 @@
+//@@author A1WAYSD-reused
+//Reused from AB-3 AddressBook.java with minor modifications
+package seedu.flashlingo.model;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import javafx.collections.ObservableList;
+import seedu.flashlingo.commons.util.ToStringBuilder;
+import seedu.flashlingo.model.flashcard.FlashCard;
+import seedu.flashlingo.model.flashcard.UniqueFlashCardList;
+
+/**
+ * Wraps all data at the flashlingo level
+ * Duplicates are not allowed (by .isSameFlash comparison)
+ */
+public class Flashlingo implements ReadOnlyFlashlingo {
+
+ private final UniqueFlashCardList flashCards;
+
+ /*
+ * 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.
+ */
+ {
+ flashCards = new UniqueFlashCardList();
+ }
+
+ public Flashlingo() {}
+
+ /**
+ * Creates a Flashlingo using the FlashCards in the {@code toBeCopied}
+ */
+ public Flashlingo(ReadOnlyFlashlingo toBeCopied) {
+ this();
+ resetData(toBeCopied);
+ }
+
+ //// list overwrite operations
+
+ /**
+ * Replaces the contents of the flashcard list with {@code flashCards}.
+ * {@code flashCards} must not contain duplicate flashCards.
+ */
+ public void setFlashCards(List flashCards) {
+ this.flashCards.setFlashCards(flashCards);
+ }
+
+ /**
+ * Resets the existing data of this {@code Flashlingo} with {@code newData}.
+ */
+ public void resetData(ReadOnlyFlashlingo newData) {
+ requireNonNull(newData);
+ setFlashCards(newData.getFlashCardList());
+ }
+
+ //// flashcard-level operations
+
+ /**
+ * Returns true if a flashcard with the same identity as {@code flashcard} exists in the flashlingo.
+ */
+ public boolean hasFlashCard(FlashCard flashCard) {
+ requireNonNull(flashCard);
+ return flashCards.contains(flashCard);
+ }
+
+ /**
+ * Adds a flashcard to Flashlingo.
+ * The flashcard must not already exist in Flashlingo.
+ */
+ public void addFlashCard(FlashCard flashCard) {
+ flashCards.add(flashCard);
+ }
+
+ /**
+ * Replaces the given flashcard {@code target} in the list with {@code editedFlashCard}.
+ * {@code target} must exist in flashlingo.
+ * The flashcard identity of {@code editedFlashCard} must not be the same as another existing
+ * flashcard in flashlingo.
+ */
+ public void setFlashCard(FlashCard target, FlashCard editedFlashCard) {
+ requireNonNull(editedFlashCard);
+
+ flashCards.setFlashCard(target, editedFlashCard);
+ }
+
+ /**
+ * Removes {@code key} from this {@code Flashlingo}.
+ * {@code key} must exist in Flashlingo.
+ */
+ public void removeFlashCard(FlashCard key) {
+ flashCards.remove(key);
+ }
+
+ //// util methods
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("FlashCards", flashCards)
+ .toString();
+ }
+
+ @Override
+ public ObservableList getFlashCardList() {
+ return flashCards.asUnmodifiableObservableList();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Flashlingo)) {
+ return false;
+ }
+
+ Flashlingo otherFlashlingo = (Flashlingo) other;
+ return flashCards.equals(otherFlashlingo.flashCards);
+ }
+
+ @Override
+ public int hashCode() {
+ return flashCards.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/Model.java b/src/main/java/seedu/flashlingo/model/Model.java
new file mode 100644
index 00000000000..2733017ce28
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/Model.java
@@ -0,0 +1,137 @@
+//@@author
+package seedu.flashlingo.model;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+import javafx.collections.ObservableList;
+import seedu.flashlingo.commons.core.GuiSettings;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.model.flashcard.FlashCard;
+import seedu.flashlingo.model.flashcard.words.TranslatedWord;
+
+/**
+ * The API of the Model component.
+ */
+public interface Model {
+ /**
+ * {@code Predicate} that always evaluate to true
+ */
+ Predicate PREDICATE_SHOW_ALL_FLASHCARDS = unused -> true;
+
+ /**
+ * Replaces user prefs data with the data in {@code userPrefs}.
+ */
+ void setUserPrefs(ReadOnlyUserPrefs userPrefs);
+
+ /**
+ * Returns the user prefs.
+ */
+ ReadOnlyUserPrefs getUserPrefs();
+
+ /**
+ * Returns the user prefs' GUI settings.
+ */
+ GuiSettings getGuiSettings();
+
+ /**
+ * Sets the user prefs' GUI settings.
+ */
+ void setGuiSettings(GuiSettings guiSettings);
+
+ //@@author A1WAYSD
+ /**
+ * Returns the user prefs' theme.
+ */
+ String getTheme();
+
+ /**
+ * Sets the user prefs' theme.
+ */
+ void setTheme(String theme);
+
+ /**
+ * Switches the user prefs' theme between light and dark.
+ */
+ void switchTheme();
+
+ //@@author WangCheng0116
+ /**
+ * Returns the user prefs' Flashlingo file path.
+ */
+ Path getFlashlingoFilePath();
+
+ /**
+ * Sets the user prefs' Flashlingo file path.
+ */
+ void setFlashlingoFilePath(Path flashlingoFilePath);
+
+ /**
+ * Replaces Flashlingo data with the data in {@code flashlingo}.
+ */
+ void setFlashlingo(ReadOnlyFlashlingo flashlingo);
+
+ /**
+ * Returns the Flashlingo
+ */
+ ReadOnlyFlashlingo getFlashlingo();
+
+ /**
+ * Returns true if a flashcard with the same identity as {@code flashcard} exists in the Flashlingo.
+ */
+ boolean hasFlashCard(FlashCard flashCard);
+
+ /**
+ * Deletes the given flashcard.
+ * The flashcard must exist in the Flashlingo.
+ */
+ void deleteFlashCard(FlashCard target);
+
+ /**
+ * Adds the given FlashCard.
+ * {@code flashCard} must not already exist in the Flashlingo.
+ */
+ void addFlashCard(FlashCard flashCard);
+
+ //@@author A1WAYSD
+ void addFlashCards(ArrayList flashCards);
+
+ //@@author WangCheng0116
+ /**
+ * Replaces the given flashcard {@code target} with {@code editedFlashCard}.
+ * {@code target} must exist in the Flashlingo .
+ * The flashcard identity of {@code editedFlashCard} must not be the same as another existing
+ * FlashCard in the Flashlingo.
+ */
+ void setFlashCard(FlashCard target, FlashCard editedFlashCard);
+
+ /**
+ * Returns an unmodifiable view of the filtered flashcard list
+ */
+ ObservableList getFilteredFlashCardList();
+
+ /**
+ * Updates the filter of the filtered flashcard list to filter by the given {@code predicate}.
+ *
+ * @throws NullPointerException if {@code predicate} is null.
+ */
+ void updateFilteredFlashCardList(Predicate predicate);
+
+ /**
+ * Evaluates and returns the number of FlashCards
+ * @return Number of FlashCards
+ */
+ int getNumberOfFlashCards();
+
+ /**
+ * Evaluated the number of FlashCards remebered in this session
+ * @return Number of FlashCardsRemembered
+ */
+ int getNumberOfRememberedWords();
+ FlashCard nextReviewWord() throws CommandException;
+ void startSession() throws CommandException;
+ void endSession();
+ boolean hasNextRound();
+ TranslatedWord reveal(FlashCard toBeRevealed);
+}
diff --git a/src/main/java/seedu/flashlingo/model/ModelManager.java b/src/main/java/seedu/flashlingo/model/ModelManager.java
new file mode 100644
index 00000000000..97e6542708c
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/ModelManager.java
@@ -0,0 +1,245 @@
+package seedu.flashlingo.model;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.flashlingo.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.function.Predicate;
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.collections.transformation.FilteredList;
+import seedu.flashlingo.commons.core.GuiSettings;
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.logic.session.SessionManager;
+import seedu.flashlingo.model.flashcard.FlashCard;
+import seedu.flashlingo.model.flashcard.NextReviewWordPredicate;
+import seedu.flashlingo.model.flashcard.WordOverduePredicate;
+import seedu.flashlingo.model.flashcard.words.TranslatedWord;
+
+/**
+ * Represents the in-memory model of the flashlingo data.
+ */
+public class ModelManager implements Model {
+ private static final Logger logger = LogsCenter.getLogger(ModelManager.class);
+ private final Flashlingo flashlingo;
+ private final UserPrefs userPrefs;
+ private final FilteredList filteredFlashCards;
+
+ /**
+ * Initializes a ModelManager with the given flashlingo and userPrefs.
+ */
+ public ModelManager(ReadOnlyFlashlingo flashlingo, ReadOnlyUserPrefs userPrefs) {
+ requireAllNonNull(flashlingo, userPrefs);
+
+ logger.fine("Initializing with Flashlingo: " + flashlingo + " and user prefs " + userPrefs);
+
+ this.flashlingo = new Flashlingo(flashlingo);
+ this.userPrefs = new UserPrefs(userPrefs);
+ filteredFlashCards = new FilteredList<>(this.flashlingo.getFlashCardList());
+ }
+
+ public ModelManager() {
+ this(new Flashlingo(), new UserPrefs());
+ }
+
+ //=========== UserPrefs ==================================================================================
+
+ //@@author
+ @Override
+ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
+ requireNonNull(userPrefs);
+ this.userPrefs.resetData(userPrefs);
+ }
+
+ @Override
+ public ReadOnlyUserPrefs getUserPrefs() {
+ return userPrefs;
+ }
+
+ @Override
+ public GuiSettings getGuiSettings() {
+ return userPrefs.getGuiSettings();
+ }
+
+ @Override
+ public void setGuiSettings(GuiSettings guiSettings) {
+ requireNonNull(guiSettings);
+ userPrefs.setGuiSettings(guiSettings);
+ }
+
+ @Override
+ public Path getFlashlingoFilePath() {
+ return userPrefs.getFlashlingoFilePath();
+ }
+
+ @Override
+ public void setFlashlingoFilePath(Path flashlingoFilePath) {
+ requireNonNull(flashlingoFilePath);
+ userPrefs.setFlashlingoFilePath(flashlingoFilePath);
+ }
+
+ @Override
+ public String getTheme() {
+ return userPrefs.getTheme();
+ }
+
+ @Override
+ public void setTheme(String theme) {
+ requireNonNull(theme);
+ userPrefs.setTheme(theme);
+ }
+
+ @Override
+ public void switchTheme() {
+ if (getTheme().equals("Default")) {
+ setTheme("Dark");
+ } else {
+ setTheme("Default");
+ }
+ }
+
+ //=========== Flashlingo ================================================================================
+
+ @Override
+ public void setFlashlingo(ReadOnlyFlashlingo flashlingo) {
+ this.flashlingo.resetData(flashlingo);
+ }
+
+ @Override
+ public ReadOnlyFlashlingo getFlashlingo() {
+ return flashlingo;
+ }
+
+ @Override
+ public boolean hasFlashCard(FlashCard flashCard) {
+ requireNonNull(flashCard);
+ return flashlingo.hasFlashCard(flashCard);
+ }
+
+ @Override
+ public void deleteFlashCard(FlashCard target) {
+ flashlingo.removeFlashCard(target);
+ }
+
+ @Override
+ public void addFlashCard(FlashCard flashCard) {
+ flashlingo.addFlashCard(flashCard);
+ updateFilteredFlashCardList(PREDICATE_SHOW_ALL_FLASHCARDS);
+ }
+
+ @Override
+ public void addFlashCards(ArrayList flashCards) {
+ for (FlashCard flashCard : flashCards) {
+ if (flashlingo.hasFlashCard(flashCard)) {
+ continue;
+ }
+ flashlingo.addFlashCard(flashCard);
+ }
+ updateFilteredFlashCardList(PREDICATE_SHOW_ALL_FLASHCARDS);
+ }
+
+ @Override
+ public void setFlashCard(FlashCard target, FlashCard editedFlashCard) {
+ requireAllNonNull(target, editedFlashCard);
+ flashlingo.setFlashCard(target, editedFlashCard);
+ }
+ @Override
+ public int getNumberOfFlashCards() {
+ return this.filteredFlashCards.size();
+ }
+
+ @Override
+ public FlashCard nextReviewWord() throws CommandException {
+ updateFilteredFlashCardList(new WordOverduePredicate());
+ if (filteredFlashCards.size() == 0) {
+ SessionManager.getInstance().setSession(false);
+ updateFilteredFlashCardList(unused -> true);
+ throw new CommandException("There's no FlashCards to review. Well done!");
+ }
+ FlashCard toBeReviewed = getFilteredFlashCardList().get(0);
+ Predicate t = new NextReviewWordPredicate(toBeReviewed);
+ updateFilteredFlashCardList(t);
+ return toBeReviewed;
+ }
+
+ //=========== Filtered Flashcard List Accessors =============================================================
+
+ /**
+ * Returns an unmodifiable view of the list of {@code FlashCard} backed by the internal list of
+ * {@code versionedFlashlingo}
+ */
+ @Override
+ public ObservableList getFilteredFlashCardList() {
+ return filteredFlashCards;
+ }
+
+ @Override
+ public void updateFilteredFlashCardList(Predicate predicate) {
+ requireNonNull(predicate);
+ filteredFlashCards.setPredicate(predicate);
+ }
+
+ @Override
+ public int getNumberOfRememberedWords() {
+ int numOfFlashCardsRemembered = 0;
+ for (FlashCard flashCard : filteredFlashCards) {
+ if (flashCard.isRecalled()) {
+ numOfFlashCardsRemembered += 1;
+ }
+ }
+ return numOfFlashCardsRemembered;
+ }
+ @Override
+ public void startSession() throws CommandException {
+ SessionManager.getInstance().setSession(true);
+ updateFilteredFlashCardList(new WordOverduePredicate());
+ if (getFilteredFlashCardList().size() == 0) {
+ SessionManager.getInstance().setSession(false);
+ updateFilteredFlashCardList(PREDICATE_SHOW_ALL_FLASHCARDS);
+ throw new CommandException("You have no more words to review!");
+ }
+ updateFilteredFlashCardList(new NextReviewWordPredicate(getFilteredFlashCardList().get(0)));
+ }
+ @Override
+ public void endSession() {
+ SessionManager.getInstance().setSession(false);
+ updateFilteredFlashCardList(PREDICATE_SHOW_ALL_FLASHCARDS);
+ }
+
+ @Override
+ public boolean hasNextRound() {
+ updateFilteredFlashCardList(new WordOverduePredicate());
+ return getFilteredFlashCardList().size() != 0;
+ }
+
+ @Override
+ public TranslatedWord reveal(FlashCard toBeRevealed) {
+ if (toBeRevealed.getIsRevealed()) {
+ toBeRevealed.setIsRevealed(false);
+ } else {
+ toBeRevealed.setIsRevealed(true);
+ }
+ return toBeRevealed.getTranslatedWord();
+ }
+
+ //@@author
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ModelManager)) {
+ return false;
+ }
+
+ ModelManager otherModelManager = (ModelManager) other;
+ return flashlingo.equals(otherModelManager.flashlingo)
+ && userPrefs.equals(otherModelManager.userPrefs)
+ && filteredFlashCards.equals(otherModelManager.filteredFlashCards);
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/ReadOnlyFlashlingo.java b/src/main/java/seedu/flashlingo/model/ReadOnlyFlashlingo.java
new file mode 100644
index 00000000000..2be4e76e3a6
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/ReadOnlyFlashlingo.java
@@ -0,0 +1,19 @@
+//@@author A1WAYSD-reused
+//Reused from AB-3 ReadOnlyAddressBook.java with minor modifications
+package seedu.flashlingo.model;
+
+import javafx.collections.ObservableList;
+import seedu.flashlingo.model.flashcard.FlashCard;
+
+/**
+ * Unmodifiable view of a flashlingo.
+ */
+public interface ReadOnlyFlashlingo {
+
+ /**
+ * Returns an unmodifiable view of the flash cards list.
+ * This list will not contain any duplicate flash cards.
+ */
+ ObservableList getFlashCardList();
+
+}
diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/flashlingo/model/ReadOnlyUserPrefs.java
similarity index 51%
rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
rename to src/main/java/seedu/flashlingo/model/ReadOnlyUserPrefs.java
index befd58a4c73..0f02da0957a 100644
--- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
+++ b/src/main/java/seedu/flashlingo/model/ReadOnlyUserPrefs.java
@@ -1,8 +1,8 @@
-package seedu.address.model;
+package seedu.flashlingo.model;
import java.nio.file.Path;
-import seedu.address.commons.core.GuiSettings;
+import seedu.flashlingo.commons.core.GuiSettings;
/**
* Unmodifiable view of user prefs.
@@ -11,6 +11,8 @@ public interface ReadOnlyUserPrefs {
GuiSettings getGuiSettings();
- Path getAddressBookFilePath();
+ Path getFlashlingoFilePath();
+
+ String getTheme();
}
diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/flashlingo/model/UserPrefs.java
similarity index 61%
rename from src/main/java/seedu/address/model/UserPrefs.java
rename to src/main/java/seedu/flashlingo/model/UserPrefs.java
index 6be655fb4c7..d6dd20b9cf6 100644
--- a/src/main/java/seedu/address/model/UserPrefs.java
+++ b/src/main/java/seedu/flashlingo/model/UserPrefs.java
@@ -1,4 +1,4 @@
-package seedu.address.model;
+package seedu.flashlingo.model;
import static java.util.Objects.requireNonNull;
@@ -6,15 +6,15 @@
import java.nio.file.Paths;
import java.util.Objects;
-import seedu.address.commons.core.GuiSettings;
+import seedu.flashlingo.commons.core.GuiSettings;
/**
* Represents User's preferences.
*/
public class UserPrefs implements ReadOnlyUserPrefs {
-
private GuiSettings guiSettings = new GuiSettings();
- private Path addressBookFilePath = Paths.get("data" , "addressbook.json");
+ private Path flashlingoFilePath = Paths.get("data" , "flashlingo.json");
+ private String theme = "Default";
/**
* Creates a {@code UserPrefs} with default values.
@@ -35,7 +35,8 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) {
public void resetData(ReadOnlyUserPrefs newUserPrefs) {
requireNonNull(newUserPrefs);
setGuiSettings(newUserPrefs.getGuiSettings());
- setAddressBookFilePath(newUserPrefs.getAddressBookFilePath());
+ setFlashlingoFilePath(newUserPrefs.getFlashlingoFilePath());
+ setTheme(newUserPrefs.getTheme());
}
public GuiSettings getGuiSettings() {
@@ -47,13 +48,22 @@ public void setGuiSettings(GuiSettings guiSettings) {
this.guiSettings = guiSettings;
}
- public Path getAddressBookFilePath() {
- return addressBookFilePath;
+ public void setTheme(String theme) {
+ requireNonNull(theme);
+ this.theme = theme;
+ }
+
+ public String getTheme() {
+ return theme;
}
- public void setAddressBookFilePath(Path addressBookFilePath) {
- requireNonNull(addressBookFilePath);
- this.addressBookFilePath = addressBookFilePath;
+ public Path getFlashlingoFilePath() {
+ return flashlingoFilePath;
+ }
+
+ public void setFlashlingoFilePath(Path flashlingoFilePath) {
+ requireNonNull(flashlingoFilePath);
+ this.flashlingoFilePath = flashlingoFilePath;
}
@Override
@@ -69,19 +79,22 @@ public boolean equals(Object other) {
UserPrefs otherUserPrefs = (UserPrefs) other;
return guiSettings.equals(otherUserPrefs.guiSettings)
- && addressBookFilePath.equals(otherUserPrefs.addressBookFilePath);
+ && flashlingoFilePath.equals(otherUserPrefs.flashlingoFilePath)
+ && theme.equals(otherUserPrefs.theme);
+
}
@Override
public int hashCode() {
- return Objects.hash(guiSettings, addressBookFilePath);
+ return Objects.hash(guiSettings, flashlingoFilePath, theme);
}
@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 : " + flashlingoFilePath);
+ sb.append("\nTheme : " + theme);
return sb.toString();
}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/FlashCard.java b/src/main/java/seedu/flashlingo/model/flashcard/FlashCard.java
new file mode 100644
index 00000000000..bef1917e45c
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/FlashCard.java
@@ -0,0 +1,212 @@
+//@@author itsNatTan
+
+package seedu.flashlingo.model.flashcard;
+
+import static seedu.flashlingo.commons.util.AppUtil.checkArgument;
+import static seedu.flashlingo.logic.Messages.MESSAGE_SAME_WORD;
+
+import java.util.Date;
+
+import seedu.flashlingo.model.flashcard.words.OriginalWord;
+import seedu.flashlingo.model.flashcard.words.TranslatedWord;
+
+/**
+ * Represents each flashcard
+ *
+ * @author Nathanael M. Tan
+ * @version 1.0
+ * @since 1.0
+ */
+public class FlashCard {
+ private final OriginalWord originalWord;
+ private final TranslatedWord translatedWord;
+ private Date whenToReview; // Date the flashcard was needs to be reviewed
+ private ProficiencyLevel currentLevel; // How many times successfully remembered
+ private boolean isRemembered; //if successfully remembers word
+ private boolean isRevealed = false;
+ /**
+ * Constructor for Flashcard
+ *
+ * @param originalWord The word in the original language
+ * @param translatedWord The word in the language you are learning
+ * @param whenToReview The date of when you need to review this word
+ * @param level The level of familiarity with the word
+ */
+ public FlashCard(OriginalWord originalWord, TranslatedWord translatedWord, Date whenToReview,
+ ProficiencyLevel level) {
+ this.currentLevel = level;
+ this.whenToReview = whenToReview;
+ this.translatedWord = translatedWord;
+ this.originalWord = originalWord;
+ checkArgument(isValidWord(originalWord, translatedWord), MESSAGE_SAME_WORD);
+ }
+
+ /**
+ * Constructor for Flashcard
+ *
+ * @param originalWord The word in the original language
+ * @param translatedWord The word in the language you are learning
+ * @param whenToReview The date of when you need to review this word
+ * @param level The level of familiarity with the word
+ * @param isRemembered Whether the word was remembered
+ */
+ public FlashCard(OriginalWord originalWord, TranslatedWord translatedWord, Date whenToReview,
+ ProficiencyLevel level, boolean isRemembered) {
+ this.currentLevel = level;
+ this.whenToReview = whenToReview;
+ this.translatedWord = translatedWord;
+ this.originalWord = originalWord;
+ this.isRemembered = isRemembered;
+ }
+ //@@author D-Limiter
+ public OriginalWord getOriginalWord() {
+ return originalWord;
+ }
+ //@@author D-Limiter
+ public TranslatedWord getTranslatedWord() {
+ return translatedWord;
+ }
+ //@@author
+ public Date getWhenToReview() {
+ return whenToReview;
+ }
+
+ public ProficiencyLevel getProficiencyLevel() {
+ return currentLevel;
+ }
+
+ public boolean isDeletedFromReview() {
+ return this.currentLevel.isDeletedFromReview();
+ }
+
+ public void setIsRevealed(Boolean isRevealed) {
+ this.isRevealed = isRevealed;
+ }
+ public boolean getIsRevealed() {
+ return this.isRevealed;
+ }
+
+ //@@author Song-Mengfei
+ private boolean isValidWord(OriginalWord word, TranslatedWord translate) {
+ return !word.getWord().equalsIgnoreCase(translate.getWord());
+ }
+ /**
+ * Edits the flashCard
+ * @param changes The new word to replace the old word
+ * @return The new flashcard
+ */
+ public FlashCard editFlashCard(String[] changes) {
+ OriginalWord originalWord = this.originalWord.editWord(changes[0], changes[1]);
+ TranslatedWord translatedWord = this.translatedWord.editWord(changes[2], changes[3]);
+ return new FlashCard(originalWord, translatedWord, whenToReview, currentLevel);
+ }
+
+ /**
+ * Returns true if both flashcards have the same originalWord and translatedWord.
+ * This defines a weaker notion of equality between two flashcards.
+ */
+ public boolean isSameFlashCard(FlashCard otherFlashCard) {
+ if (otherFlashCard == this) {
+ return true;
+ }
+
+ return otherFlashCard != null
+ && otherFlashCard.getOriginalWord().equals(getOriginalWord())
+ && otherFlashCard.getTranslatedWord().equals(getTranslatedWord());
+ }
+
+ //@@author D-Limiter
+ /**
+ * Returns true if the original word or the translation contains the keyword.
+ * @param inputWord The keyword to check for
+ * @return True or False depending on whether the keyword is found
+ */
+ public boolean hasKeyword(String inputWord) {
+ return this.originalWord.hasSubpart(inputWord) || this.translatedWord.hasSubpart(inputWord);
+ }
+
+ //@@author D-Limiter
+ /**
+ * Sets this FlashCard as remembered
+ */
+ public void recallFlashCard() {
+ this.isRemembered = true;
+ }
+ //@@author D-Limiter
+ /**
+ * Sets this FlashCard as forgotten
+ */
+ public void forgetFlashCard() {
+ this.isRemembered = false;
+ }
+ //@@author D-Limiter
+ /**
+ * Evaluates and returns if this FlashCard is remembered
+ */
+ public boolean isRecalled() {
+ return this.isRemembered;
+ }
+
+ /**
+ * Returns true if the original word or the translation is of the language.
+ * @param language The language to check for
+ * @return True or False depending on whether the language is found
+ */
+ public boolean isSameLanguage(String language) {
+ return this.originalWord.isSameLanguage(language) || this.translatedWord.isSameLanguage(language);
+ }
+
+ //@@author itsNatTan
+ /**
+ * Returns true if the review date is before the current date.
+ * @return True or False depending on whether the review date is before the current date
+ */
+ public boolean isOverdue() {
+ return this.whenToReview.before(new Date());
+ }
+
+ /**
+ * Update the flash card to next level
+ */
+ public void updateLevel(boolean isSuccess) {
+ if (isSuccess) {
+ getProficiencyLevel().upgradeLevel();
+ updateReviewDate(getProficiencyLevel().calculateNextReviewInterval());
+ } else {
+ getProficiencyLevel().downgradeLevel();
+ updateReviewDate(getProficiencyLevel().calculateNextReviewInterval());
+ }
+ }
+
+ /**
+ * Formats Flashcard for writing to textFile
+ *
+ * @return Tab separated String formatted for writing
+ */
+ @Override
+ public String toString() {
+ String sb = originalWord + " | " + originalWord.getLanguage() + " | " + translatedWord + " | "
+ + translatedWord.getLanguage() + " | " + whenToReview.toString() + " | " + currentLevel + "\n";
+ return sb;
+ }
+
+ public void updateReviewDate(long timeInMs) {
+ this.whenToReview = new Date(new Date().getTime() + timeInMs);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FlashCard)) {
+ return false;
+ }
+
+ FlashCard otherFlashCard = (FlashCard) other;
+ return originalWord.equals(otherFlashCard.originalWord)
+ && translatedWord.equals(otherFlashCard.translatedWord);
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/NextReviewWordPredicate.java b/src/main/java/seedu/flashlingo/model/flashcard/NextReviewWordPredicate.java
new file mode 100644
index 00000000000..451ce57609d
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/NextReviewWordPredicate.java
@@ -0,0 +1,35 @@
+package seedu.flashlingo.model.flashcard;
+
+import java.util.function.Predicate;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+
+/**
+ * Gets the first flashcard.
+ */
+public class NextReviewWordPredicate implements Predicate {
+
+ private final FlashCard flashCard;
+ public NextReviewWordPredicate(FlashCard flashCard) {
+ this.flashCard = flashCard;
+ }
+
+ @Override
+ public boolean test(FlashCard flashCard) {
+ return this.flashCard.equals(flashCard);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ return other instanceof WordOverduePredicate;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("to be reviewed", "flashcard").toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/ProficiencyLevel.java b/src/main/java/seedu/flashlingo/model/flashcard/ProficiencyLevel.java
new file mode 100644
index 00000000000..2f6e48f488c
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/ProficiencyLevel.java
@@ -0,0 +1,103 @@
+//@@author itsNatTan
+
+package seedu.flashlingo.model.flashcard;
+
+/**
+ * Represents the level of familiarity to a flashcard
+ *
+ * @author Nathanael M. Tan
+ * @version 1.2
+ * @since 1.2
+ */
+public class ProficiencyLevel {
+ public static final String MESSAGE_CONSTRAINTS =
+ "Proficiency level should be an integer larger than or equal to 1";
+
+ /**
+ * The threshold after which the flashcard is removed from review session
+ */
+ private static int deleteFromReviewThreshold = 5;
+ private int level; // Base level of 1
+
+ public ProficiencyLevel(int level) {
+ this.level = level;
+ }
+
+ /**
+ * Returns the time interval to the next review of the flashcard
+ * Based upon the Leitner System, to study with spaced repetition
+ * @return
+ */
+ public long calculateNextReviewInterval() {
+ return this.level * 86400000L;
+ }
+
+ /**
+ * Upon successfully remembering the flashcard, increment level to increase review interval
+ */
+ public void upgradeLevel() {
+ this.level += 1;
+ }
+
+ /**
+ * Upon failure to remember, downgrade level to decrease interval to review
+ */
+ public void downgradeLevel() {
+ if (level != 1) { // Prevent going below base level
+ this.level -= 1;
+ }
+ }
+
+ /**
+ * Checks if the flashcard is deleted from review session.
+ * @return
+ */
+ public boolean isDeletedFromReview() {
+ if (level >= deleteFromReviewThreshold) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the level of proficiency
+ * @return The level of proficiency.
+ */
+ public int getLevel() {
+ return level;
+ }
+
+ @Override
+ public String toString() {
+ if (this.level >= this.deleteFromReviewThreshold) {
+ return "Word Mastered";
+ }
+ return String.valueOf(this.level);
+ }
+
+ public static boolean isValidProficiencyLevel(int level) {
+ return level >= 1;
+ }
+
+ public void setLevel(int level) {
+ this.level = level;
+ }
+
+ /**
+ * Checks if two proficiency levels are equal.
+ * @param other
+ * @return
+ */
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ProficiencyLevel)) {
+ return false;
+ }
+ ProficiencyLevel otherProficiencyLevel = (ProficiencyLevel) other;
+ return otherProficiencyLevel.level == this.level;
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/UniqueFlashCardList.java b/src/main/java/seedu/flashlingo/model/flashcard/UniqueFlashCardList.java
new file mode 100644
index 00000000000..02be1e532d8
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/UniqueFlashCardList.java
@@ -0,0 +1,150 @@
+//@@author A1WAYSD-reused
+//Reused from AB-3 UniquePersonList.java with minor modifications
+package seedu.flashlingo.model.flashcard;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.flashlingo.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.flashlingo.model.flashcard.exceptions.DuplicateFlashCardException;
+import seedu.flashlingo.model.flashcard.exceptions.FlashCardNotFoundException;
+
+/**
+ * A list of flashcards that enforces uniqueness between its elements and does not allow nulls.
+ * A flashcard is considered unique by comparing using {@code FlashCard#isSameFlashCard(FlashCard)}.
+ * As such, adding and updating of
+ * flashcards uses FlashCard#isSameFlashCard(FlashCard)
+ * for equality so as to ensure that the flashcard being added or updated is
+ * unique in terms of identity in the UniqueFlashCardList.
+ * However, the removal of a flashcard uses FlashCard#equals(Object) so
+ * as to ensure that the flashcard with exactly the same fields will be removed.
+ *
+ * Supports a minimal set of list operations.
+ *
+ * @see FlashCard#isSameFlashCard(FlashCard)
+ */
+public class UniqueFlashCardList implements Iterable {
+
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent flashcard as the given argument.
+ */
+ public boolean contains(FlashCard toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameFlashCard);
+ }
+
+ /**
+ * Adds a flashcard to the list.
+ * The flashcard must not already exist in the list.
+ */
+ public void add(FlashCard toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateFlashCardException();
+ }
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Replaces the flashcard {@code target} in the list with {@code editedFlashCard}.
+ * {@code target} must exist in the list.
+ * The flashcard identity of {@code editedFlashCard} must not be the same as another existing flashcard in the list.
+ */
+ public void setFlashCard(FlashCard target, FlashCard editedFlashCard) {
+ requireAllNonNull(target, editedFlashCard);
+
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new FlashCardNotFoundException();
+ }
+
+ if (!target.isSameFlashCard(editedFlashCard) && contains(editedFlashCard)) {
+ throw new DuplicateFlashCardException();
+ }
+
+ internalList.set(index, editedFlashCard);
+ }
+
+ /**
+ * Removes the equivalent flashcard from the list.
+ * The flashcard must exist in the list.
+ */
+ public void remove(FlashCard toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new FlashCardNotFoundException();
+ }
+ }
+
+ /**
+ * Replaces the contents of this list with {@code flashCards}.
+ * {@code flashCards} must not contain duplicate flashCards.
+ */
+ public void setFlashCards(List flashCards) {
+ requireAllNonNull(flashCards);
+ if (!flashCardsAreUnique(flashCards)) {
+ throw new DuplicateFlashCardException();
+ }
+
+ internalList.setAll(flashCards);
+ }
+
+ /**
+ * 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) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UniqueFlashCardList)) {
+ return false;
+ }
+
+ UniqueFlashCardList otherUniqueFlashCardList = (UniqueFlashCardList) other;
+ return internalList.equals(otherUniqueFlashCardList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+
+ /**
+ * Returns true if {@code flashCards} contains only unique flashCards.
+ */
+ private boolean flashCardsAreUnique(List cardList) {
+ for (int i = 0; i < cardList.size() - 1; i++) {
+ for (int j = i + 1; j < cardList.size(); j++) {
+ if (cardList.get(i).isSameFlashCard(cardList.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/WordContainsKeywordsPredicate.java b/src/main/java/seedu/flashlingo/model/flashcard/WordContainsKeywordsPredicate.java
new file mode 100644
index 00000000000..26b57475301
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/WordContainsKeywordsPredicate.java
@@ -0,0 +1,43 @@
+package seedu.flashlingo.model.flashcard;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+
+/**
+ * Tests that a {@code FlashCard}'s {@code Word} matches any of the keywords given.
+ */
+public class WordContainsKeywordsPredicate implements Predicate {
+ private final List keywords;
+
+ public WordContainsKeywordsPredicate(List keywords) {
+ this.keywords = keywords;
+ }
+
+ @Override
+ public boolean test(FlashCard flashCard) {
+ return keywords.stream()
+ .anyMatch(flashCard::hasKeyword);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof WordContainsKeywordsPredicate)) {
+ return false;
+ }
+
+ WordContainsKeywordsPredicate otherNameContainsKeywordsPredicate = (WordContainsKeywordsPredicate) other;
+ return keywords.equals(otherNameContainsKeywordsPredicate.keywords);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("keywords", keywords).toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/WordLanguagePredicate.java b/src/main/java/seedu/flashlingo/model/flashcard/WordLanguagePredicate.java
new file mode 100644
index 00000000000..f37e48ac8de
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/WordLanguagePredicate.java
@@ -0,0 +1,41 @@
+package seedu.flashlingo.model.flashcard;
+
+import java.util.function.Predicate;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+
+/**
+ * Tests that a {@code FlashCard} is the language given.
+ */
+public class WordLanguagePredicate implements Predicate {
+ private final String language;
+
+ public WordLanguagePredicate(String language) {
+ this.language = language;
+ }
+
+ @Override
+ public boolean test(FlashCard flashCard) {
+ return flashCard.isSameLanguage(language);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof WordLanguagePredicate)) {
+ return false;
+ }
+
+ WordLanguagePredicate otherWordLanguagePredicate = (WordLanguagePredicate) other;
+ return language.equals(otherWordLanguagePredicate.language);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("keywords", language).toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/WordOverduePredicate.java b/src/main/java/seedu/flashlingo/model/flashcard/WordOverduePredicate.java
new file mode 100644
index 00000000000..c2e437ffe11
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/WordOverduePredicate.java
@@ -0,0 +1,33 @@
+package seedu.flashlingo.model.flashcard;
+
+import java.util.function.Predicate;
+
+import seedu.flashlingo.commons.util.ToStringBuilder;
+
+/**
+ * Tests that a {@code FlashCard} is overdue or not.
+ */
+public class WordOverduePredicate implements Predicate {
+
+ public WordOverduePredicate() {
+ }
+
+ @Override
+ public boolean test(FlashCard flashCard) {
+ return flashCard.isOverdue() && !flashCard.isDeletedFromReview();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ return other instanceof WordOverduePredicate;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("keywords", "overdue").toString();
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/exceptions/DuplicateFlashCardException.java b/src/main/java/seedu/flashlingo/model/flashcard/exceptions/DuplicateFlashCardException.java
new file mode 100644
index 00000000000..679f9d43687
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/exceptions/DuplicateFlashCardException.java
@@ -0,0 +1,12 @@
+package seedu.flashlingo.model.flashcard.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate FlashCards
+ * (FlashCards are considered duplicates if they have the same
+ * originalWord and Translation).
+ */
+public class DuplicateFlashCardException extends RuntimeException {
+ public DuplicateFlashCardException() {
+ super("Operation would result in duplicate flashcards");
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/exceptions/FlashCardNotFoundException.java b/src/main/java/seedu/flashlingo/model/flashcard/exceptions/FlashCardNotFoundException.java
new file mode 100644
index 00000000000..ded8204e24a
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/exceptions/FlashCardNotFoundException.java
@@ -0,0 +1,6 @@
+package seedu.flashlingo.model.flashcard.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified flash card.
+ */
+public class FlashCardNotFoundException extends RuntimeException {}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/words/OriginalWord.java b/src/main/java/seedu/flashlingo/model/flashcard/words/OriginalWord.java
new file mode 100644
index 00000000000..95c2da584d6
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/words/OriginalWord.java
@@ -0,0 +1,66 @@
+package seedu.flashlingo.model.flashcard.words;
+
+import static seedu.flashlingo.logic.Messages.MESSAGE_EMPTY_VALUE;
+
+/**
+ * Represents the original word
+ *
+ * @author Nathanael M. Tan, Taanish Bhardwaj
+ * @version 1.2
+ * @since 1.2
+ */
+public class OriginalWord extends Word {
+ /**
+ * Constructs a new Original Word
+ * @param word String to be encapsulated by this Original Word
+ * @param language Language of the encapsulated word
+ */
+ public OriginalWord(String word, String language) {
+ super(word, language);
+ }
+
+ /**
+ * Constructs a new Original Word
+ * @param word String to be encapsulated by this Original Word
+ */
+ public OriginalWord(String word) {
+ super(word, "");
+ }
+
+ /**
+ * Edits this word
+ * @param newWord The new word to replace this word
+ * @return The new word
+ */
+ @Override
+ public OriginalWord editWord(String newWord, String newLanguage) {
+ if (newWord == null) {
+ newWord = getWord();
+ } else if (newWord.isEmpty()) {
+ throw new IllegalArgumentException(MESSAGE_EMPTY_VALUE);
+ }
+ return new OriginalWord(
+ newWord, newLanguage == null ? getLanguage() : newLanguage);
+ }
+
+ /**
+ * Checks whether this Original Word is equal to the passed object
+ * @param other Passed object to check equality against
+ * @return True or False depending on whether this and other are equal
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof OriginalWord)) {
+ return false;
+ }
+
+ OriginalWord otherOriginalWord = (OriginalWord) other;
+ return getWord().equalsIgnoreCase(otherOriginalWord.getWord())
+ && getLanguage().equalsIgnoreCase(otherOriginalWord.getLanguage());
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/words/TranslatedWord.java b/src/main/java/seedu/flashlingo/model/flashcard/words/TranslatedWord.java
new file mode 100644
index 00000000000..8d17b68c220
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/words/TranslatedWord.java
@@ -0,0 +1,65 @@
+package seedu.flashlingo.model.flashcard.words;
+
+import static seedu.flashlingo.logic.Messages.MESSAGE_EMPTY_VALUE;
+
+/**
+ * Represents the translated word
+ *
+ * @author Nathanael M. Tan, Taanish Bhardwaj
+ * @version 1.2
+ * @since 1.2
+ */
+public class TranslatedWord extends Word {
+ /**
+ * Constructs a new Translated Word
+ * @param word String to be encapsulated by this Translated Word
+ * @param language Language of the encapsulated word
+ */
+ public TranslatedWord(String word, String language) {
+ super(word, language);
+ }
+
+ /**
+ * Constructs a new Translated Word
+ * @param word String to be encapsulated by this Translated Word
+ */
+ public TranslatedWord(String word) {
+ super(word, "");
+ }
+
+ /**
+ * Edits this word
+ * @param newWord The new word to replace this word
+ * @return The new word
+ */
+ @Override
+ public TranslatedWord editWord(String newWord, String newLanguage) {
+ if (newWord == null) {
+ newWord = getWord();
+ } else if (newWord.isEmpty()) {
+ throw new IllegalArgumentException(MESSAGE_EMPTY_VALUE);
+ }
+ return new TranslatedWord(
+ newWord, newLanguage == null ? getLanguage() : newLanguage);
+ }
+
+ /**
+ * Checks whether this Translated Word is equal to the passed object
+ * @param other Passed object to check equality against
+ * @return True or False depending on whether this and other are equal
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof TranslatedWord)) {
+ return false;
+ }
+ TranslatedWord otherTranslatedWord = (TranslatedWord) other;
+ return getWord().equalsIgnoreCase(otherTranslatedWord.getWord())
+ && getLanguage().equalsIgnoreCase(otherTranslatedWord.getLanguage());
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/model/flashcard/words/Word.java b/src/main/java/seedu/flashlingo/model/flashcard/words/Word.java
new file mode 100644
index 00000000000..9f936bbf138
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/flashcard/words/Word.java
@@ -0,0 +1,82 @@
+package seedu.flashlingo.model.flashcard.words;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Encapsulates an input word
+ */
+public abstract class Word {
+ /*
+ * The first character of the language must not be a whitespace,
+ * otherwise " " (a blank string) becomes a valid input.
+ */
+ public static final String VALIDATION_REGEX = "[\\p{Alpha} ]*";
+ /** The input word **/
+ private final String word;
+ /** The input word's language **/
+ private final String language;
+
+ /**
+ * Constructs a new Word
+ * @param word The input word
+ * @param language The input Word's language
+ */
+ public Word(String word, String language) {
+ requireNonNull(language);
+ this.word = word.trim();
+ this.language = language.trim();
+ }
+
+ /**
+ * Returns true if a given string is a valid language.
+ */
+ public static boolean isValidLanguage(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ public abstract Word editWord(String newWord, String newLanguage);
+
+ /**
+ * Evaluates and returns this word
+ * @return The word encapsulated by this
+ */
+ public String getWord() {
+ return word;
+ }
+
+ /**
+ * Evaluates and returns this word's language
+ * @return Language of this word
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * Checks whether this word contains a subpart
+ * @param subpart The phrase to check for
+ * @return True or False depending on whether this word contains subpart
+ */
+ public boolean hasSubpart(String subpart) {
+ return this.word.toLowerCase().contains(subpart.toLowerCase());
+ }
+
+ /**
+ * Checks whether this word is in the same language as the passed language
+ * @param language The language to check against
+ * @return True or False depending on whether this word is in the same language as language
+ */
+ public boolean isSameLanguage(String language) {
+ return this.language.equalsIgnoreCase(language);
+ }
+
+ /**
+ * Evaluates and returns String representation of this Word
+ * @return String representation of this Word
+ */
+ @Override
+ public String toString() {
+ return this.word + " (" + this.language + ")";
+ }
+}
+
diff --git a/src/main/java/seedu/flashlingo/model/util/SampleDataUtil.java b/src/main/java/seedu/flashlingo/model/util/SampleDataUtil.java
new file mode 100644
index 00000000000..ea5b4a1eee9
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/model/util/SampleDataUtil.java
@@ -0,0 +1,34 @@
+package seedu.flashlingo.model.util;
+
+import java.util.Date;
+
+import seedu.flashlingo.model.Flashlingo;
+import seedu.flashlingo.model.ReadOnlyFlashlingo;
+import seedu.flashlingo.model.flashcard.FlashCard;
+import seedu.flashlingo.model.flashcard.ProficiencyLevel;
+import seedu.flashlingo.model.flashcard.words.OriginalWord;
+import seedu.flashlingo.model.flashcard.words.TranslatedWord;
+
+/**
+ * Contains utility methods for populating {@code Flashlingo} with sample data.
+ */
+public class SampleDataUtil {
+ public static FlashCard[] getSampleFlashCards() {
+ return new FlashCard[]{
+ new FlashCard(new OriginalWord("Welcome", "English"),
+ new TranslatedWord("欢迎", "Chinese"), new Date(), new ProficiencyLevel(1)),
+ new FlashCard(new OriginalWord("to", "English"), new TranslatedWord("来到", "Chinese"),
+ new Date(), new ProficiencyLevel(1)),
+ new FlashCard(new OriginalWord("Flashlingo", "English"),
+ new TranslatedWord("闪邻国", "Chinese"), new Date(), new ProficiencyLevel(1))
+ };
+ }
+
+ public static ReadOnlyFlashlingo getSampleFlashlingo() {
+ Flashlingo sampleFl = new Flashlingo();
+ for (FlashCard sampleFlashCard : getSampleFlashCards()) {
+ sampleFl.addFlashCard(sampleFlashCard);
+ }
+ return sampleFl;
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/storage/FlashlingoStorage.java b/src/main/java/seedu/flashlingo/storage/FlashlingoStorage.java
new file mode 100644
index 00000000000..40593e2af74
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/storage/FlashlingoStorage.java
@@ -0,0 +1,48 @@
+//@@author A1WAYSD-reused
+//Reused from AB-3 AddressBookStorage.java with minor modifications
+package seedu.flashlingo.storage;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+
+import seedu.flashlingo.commons.exceptions.DataLoadingException;
+import seedu.flashlingo.model.Flashlingo;
+import seedu.flashlingo.model.ReadOnlyFlashlingo;
+
+/**
+ * Represents a storage for {@link Flashlingo}.
+ */
+public interface FlashlingoStorage {
+
+ /**
+ * Returns the file path of the data file.
+ */
+ Path getFlashlingoFilePath();
+
+ /**
+ * Returns Flashlingo data as a {@link ReadOnlyFlashlingo}.
+ * Returns {@code Optional.empty()} if storage file is not found.
+ *
+ * @throws DataLoadingException if loading the data from storage failed.
+ */
+ Optional readFlashlingo() throws DataLoadingException;
+
+ /**
+ * @see #getFlashlingoFilePath()
+ */
+ Optional readFlashlingo(Path filePath) throws DataLoadingException;
+
+ /**
+ * Saves the given {@link ReadOnlyFlashlingo} to the storage.
+ * @param flashlingo cannot be null.
+ * @throws IOException if there was any problem writing to the file.
+ */
+ void saveFlashlingo(ReadOnlyFlashlingo flashlingo) throws IOException;
+
+ /**
+ * @see #saveFlashlingo(ReadOnlyFlashlingo)
+ */
+ void saveFlashlingo(ReadOnlyFlashlingo flashlingo, Path filePath) throws IOException;
+
+}
diff --git a/src/main/java/seedu/flashlingo/storage/JsonAdaptedFlashCard.java b/src/main/java/seedu/flashlingo/storage/JsonAdaptedFlashCard.java
new file mode 100644
index 00000000000..365192c1a13
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/storage/JsonAdaptedFlashCard.java
@@ -0,0 +1,110 @@
+package seedu.flashlingo.storage;
+
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.flashlingo.commons.exceptions.IllegalValueException;
+import seedu.flashlingo.model.flashcard.FlashCard;
+import seedu.flashlingo.model.flashcard.ProficiencyLevel;
+import seedu.flashlingo.model.flashcard.words.OriginalWord;
+import seedu.flashlingo.model.flashcard.words.TranslatedWord;
+
+/**
+ * Jackson-friendly version of {@link FlashCard}.
+ */
+public class JsonAdaptedFlashCard {
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Flash card's %s field is missing!";
+ public static final String INVALID_DATE_FORMAT_MESSAGE = "Invalid date format.";
+ public static final String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ssX";
+ private final String originalWord;
+ private final String originalWordLanguage;
+ private final String translatedWord;
+ private final String translatedWordLanguage;
+ private final String whenToReview;
+ private final int level;
+
+ /**
+ * Constructs a {@code JsonAdaptedFlashCard} with the given flash card details.
+ */
+ @JsonCreator
+ public JsonAdaptedFlashCard(@JsonProperty("originalWord") String originalWord,
+ @JsonProperty("originalWordLanguage") String originalWordLanguage,
+ @JsonProperty("translatedWord") String translatedWord,
+ @JsonProperty("translatedWordLanguage") String translatedWordLanguage,
+ @JsonProperty("whenToReview") String whenToReview,
+ @JsonProperty("level") int level) {
+ this.originalWord = originalWord;
+ this.originalWordLanguage = originalWordLanguage;
+ this.translatedWord = translatedWord;
+ this.translatedWordLanguage = translatedWordLanguage;
+ this.whenToReview = whenToReview;
+ this.level = level;
+ }
+
+ /**
+ * Converts a given {@code FlashCard} into this class for Jackson use.
+ */
+ public JsonAdaptedFlashCard(FlashCard source) {
+ originalWord = source.getOriginalWord().getWord();
+ originalWordLanguage = source.getOriginalWord().getLanguage();
+ translatedWord = source.getTranslatedWord().getWord();
+ translatedWordLanguage = source.getTranslatedWord().getLanguage();
+ level = source.getProficiencyLevel().getLevel();
+
+ Date whenToReview = source.getWhenToReview();
+ ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(whenToReview.toInstant(), ZoneOffset.UTC);
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_PATTERN);
+ this.whenToReview = formatter.format(zonedDateTime);
+ }
+
+ //Solution below adapted by AB-3 JsonAdaptedPerson.java
+ /**
+ * Converts this Jackson-friendly adapted flash card object into the model's {@code FlashCard} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted flash card.
+ */
+ public FlashCard toModelType() throws IllegalValueException {
+ if (originalWord == null || originalWordLanguage == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ OriginalWord.class.getSimpleName()));
+ }
+ if (translatedWord == null || translatedWordLanguage == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ TranslatedWord.class.getSimpleName()));
+ }
+
+ final String modelOriginalWord = originalWord;
+ final String modelOriginalWordLanguage = originalWordLanguage;
+ final String modelTranslatedWord = translatedWord;
+ final String modelTranslatedWordLanguage = translatedWordLanguage;
+
+ if (whenToReview == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ Date.class.getSimpleName()));
+ }
+ try {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_PATTERN);
+ ZonedDateTime.parse(whenToReview, formatter);
+ } catch (DateTimeParseException e) {
+ throw new IllegalValueException(INVALID_DATE_FORMAT_MESSAGE);
+ }
+
+ final Date modelWhenToReview = Date.from(ZonedDateTime.parse(whenToReview).toInstant());
+
+ if (!ProficiencyLevel.isValidProficiencyLevel(level)) {
+ throw new IllegalValueException(ProficiencyLevel.MESSAGE_CONSTRAINTS);
+ }
+
+ final int modelLevel = level;
+
+ return new FlashCard(new OriginalWord(modelOriginalWord, modelOriginalWordLanguage),
+ new TranslatedWord(modelTranslatedWord, modelTranslatedWordLanguage),
+ modelWhenToReview, new ProficiencyLevel(modelLevel));
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/storage/JsonFlashlingoStorage.java b/src/main/java/seedu/flashlingo/storage/JsonFlashlingoStorage.java
new file mode 100644
index 00000000000..00873783886
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/storage/JsonFlashlingoStorage.java
@@ -0,0 +1,81 @@
+//@@author A1WAYSD-reused
+//Reused from AB-3 JsonAddressBookStorage.java with minor modifications
+package seedu.flashlingo.storage;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.commons.exceptions.DataLoadingException;
+import seedu.flashlingo.commons.exceptions.IllegalValueException;
+import seedu.flashlingo.commons.util.FileUtil;
+import seedu.flashlingo.commons.util.JsonUtil;
+import seedu.flashlingo.model.ReadOnlyFlashlingo;
+
+/**
+ * Represents a storage for {@link seedu.flashlingo.model.Flashlingo}.
+ */
+public class JsonFlashlingoStorage implements FlashlingoStorage {
+ private static final Logger logger = LogsCenter.getLogger(JsonFlashlingoStorage.class);
+ private Path filePath;
+
+ public JsonFlashlingoStorage(Path filePath) {
+ this.filePath = filePath;
+ }
+
+ public Path getFlashlingoFilePath() {
+ return filePath;
+ }
+
+ @Override
+ public Optional readFlashlingo() throws DataLoadingException {
+ return readFlashlingo(filePath);
+ }
+
+ /**
+ * Functions similarly to {@link #readFlashlingo()}.
+ *
+ * @param filePath location of the data. Cannot be null.
+ * @throws DataLoadingException if loading the data from storage failed.
+ */
+ public Optional readFlashlingo(Path filePath) throws DataLoadingException {
+ requireNonNull(filePath);
+
+ Optional jsonFlashlingo = JsonUtil.readJsonFile(
+ filePath, JsonSerializableFlashlingo.class);
+ if (!jsonFlashlingo.isPresent()) {
+ return Optional.empty();
+ }
+
+ try {
+ ReadOnlyFlashlingo flashlingo = jsonFlashlingo.get().toModelType();
+ return Optional.of(flashlingo);
+ } catch (IllegalValueException ive) {
+ logger.info("Illegal values found in " + filePath + ": " + ive.getMessage());
+ throw new DataLoadingException(ive);
+ }
+ }
+
+ @Override
+ public void saveFlashlingo(ReadOnlyFlashlingo flashlingo) throws IOException {
+ saveFlashlingo(flashlingo, filePath);
+ }
+
+ /**
+ * Functions similarly to {@link #saveFlashlingo(ReadOnlyFlashlingo)}.
+ *
+ * @param filePath location of the data. Cannot be null.
+ */
+ public void saveFlashlingo(ReadOnlyFlashlingo flashlingo, Path filePath) throws IOException {
+ requireNonNull(flashlingo);
+ requireNonNull(filePath);
+
+ FileUtil.createIfMissing(filePath);
+ JsonUtil.saveJsonFile(new JsonSerializableFlashlingo(flashlingo), filePath);
+ }
+
+}
diff --git a/src/main/java/seedu/flashlingo/storage/JsonSerializableFlashlingo.java b/src/main/java/seedu/flashlingo/storage/JsonSerializableFlashlingo.java
new file mode 100644
index 00000000000..4f56870934d
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/storage/JsonSerializableFlashlingo.java
@@ -0,0 +1,61 @@
+//@@author A1WAYSD-reused
+//Reused from AB-3 JsonSerializableAddressBook.java with minor modifications
+package seedu.flashlingo.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 seedu.flashlingo.commons.exceptions.IllegalValueException;
+import seedu.flashlingo.model.Flashlingo;
+import seedu.flashlingo.model.ReadOnlyFlashlingo;
+import seedu.flashlingo.model.flashcard.FlashCard;
+
+/**
+ * An Immutable Flashlingo that is serializable to JSON format.
+ */
+@JsonRootName(value = "flashlingo")
+class JsonSerializableFlashlingo {
+ public static final String MESSAGE_DUPLICATE_FLASHCARD = "Flashcards list contains duplicate flashcard(s).";
+ private final List flashcards = new ArrayList<>();
+
+ /**
+ * Constructs a {@code JsonSerializableFlashlingo} with the given flash cards.
+ */
+ @JsonCreator
+ public JsonSerializableFlashlingo(@JsonProperty("flashcards") List flashcards) {
+ this.flashcards.addAll(flashcards);
+ }
+
+ /**
+ * Converts a given {@code ReadOnlyFlashlingo} into this class for Jackson use.
+ *
+ * @param source future changes to this will not affect the created {@code JsonSerializableFlashlingo}.
+ */
+ public JsonSerializableFlashlingo(ReadOnlyFlashlingo source) {
+ flashcards.addAll(source.getFlashCardList().stream().map(JsonAdaptedFlashCard::new)
+ .collect(Collectors.toList()));
+ }
+
+ /**
+ * Converts this Flashlingo into the model's {@code Flashlingo} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated.
+ */
+ public Flashlingo toModelType() throws IllegalValueException {
+ Flashlingo flashlingo = new Flashlingo();
+ for (JsonAdaptedFlashCard jsonAdaptedFlashCard : flashcards) {
+ FlashCard flashCard = jsonAdaptedFlashCard.toModelType();
+ if (flashlingo.hasFlashCard(flashCard)) {
+ throw new IllegalValueException(MESSAGE_DUPLICATE_FLASHCARD);
+ }
+ flashlingo.addFlashCard(flashCard);
+ }
+ return flashlingo;
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/flashlingo/storage/JsonUserPrefsStorage.java
similarity index 82%
rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java
rename to src/main/java/seedu/flashlingo/storage/JsonUserPrefsStorage.java
index 48a9754807d..fecc5f1436c 100644
--- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java
+++ b/src/main/java/seedu/flashlingo/storage/JsonUserPrefsStorage.java
@@ -1,13 +1,13 @@
-package seedu.address.storage;
+package seedu.flashlingo.storage;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.commons.util.JsonUtil;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
+import seedu.flashlingo.commons.exceptions.DataLoadingException;
+import seedu.flashlingo.commons.util.JsonUtil;
+import seedu.flashlingo.model.ReadOnlyUserPrefs;
+import seedu.flashlingo.model.UserPrefs;
/**
* A class to access UserPrefs stored in the hard disk as a json file
diff --git a/src/main/java/seedu/flashlingo/storage/Storage.java b/src/main/java/seedu/flashlingo/storage/Storage.java
new file mode 100644
index 00000000000..71224e87f3e
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/storage/Storage.java
@@ -0,0 +1,34 @@
+//@@author A1WAYSD-reused
+//Reused from AB-3 Storage.java with minor modifications
+package seedu.flashlingo.storage;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+
+import seedu.flashlingo.commons.exceptions.DataLoadingException;
+import seedu.flashlingo.model.ReadOnlyFlashlingo;
+import seedu.flashlingo.model.ReadOnlyUserPrefs;
+import seedu.flashlingo.model.UserPrefs;
+
+/**
+ * API of the Storage component.
+ */
+public interface Storage extends FlashlingoStorage, UserPrefsStorage {
+
+ @Override
+ Optional readUserPrefs() throws DataLoadingException;
+
+ @Override
+ void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException;
+
+ @Override
+ Path getFlashlingoFilePath();
+
+ @Override
+ Optional readFlashlingo() throws DataLoadingException;
+
+ @Override
+ void saveFlashlingo(ReadOnlyFlashlingo flashlingo) throws IOException;
+
+}
diff --git a/src/main/java/seedu/flashlingo/storage/StorageManager.java b/src/main/java/seedu/flashlingo/storage/StorageManager.java
new file mode 100644
index 00000000000..301172cfbf8
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/storage/StorageManager.java
@@ -0,0 +1,78 @@
+//@@author A1WAYSD-reused
+//Reused from AB-3 StorageManager.java with minor modifications
+package seedu.flashlingo.storage;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.commons.exceptions.DataLoadingException;
+import seedu.flashlingo.model.ReadOnlyFlashlingo;
+import seedu.flashlingo.model.ReadOnlyUserPrefs;
+import seedu.flashlingo.model.UserPrefs;
+
+/**
+ * Manages storage of Flashlingo data in local storage.
+ */
+public class StorageManager implements Storage {
+ private static final Logger logger = LogsCenter.getLogger(StorageManager.class);
+ private FlashlingoStorage flashlingoStorage;
+ private UserPrefsStorage userPrefsStorage;
+
+ /**
+ * Creates a {@code StorageManager} with the given {@code FlashlingoStorage} and {@code UserPrefStorage}.
+ */
+ public StorageManager(FlashlingoStorage flashlingoStorage, UserPrefsStorage userPrefsStorage) {
+ this.flashlingoStorage = flashlingoStorage;
+ this.userPrefsStorage = userPrefsStorage;
+ }
+
+ // ================ UserPrefs methods ==============================
+
+ @Override
+ public Path getUserPrefsFilePath() {
+ return userPrefsStorage.getUserPrefsFilePath();
+ }
+
+ @Override
+ public Optional readUserPrefs() throws DataLoadingException {
+ return userPrefsStorage.readUserPrefs();
+ }
+
+ @Override
+ public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException {
+ userPrefsStorage.saveUserPrefs(userPrefs);
+ }
+
+ // ================ Flashlingo methods ==============================
+
+ @Override
+ public Path getFlashlingoFilePath() {
+ return flashlingoStorage.getFlashlingoFilePath();
+ }
+
+ @Override
+ public Optional readFlashlingo() throws DataLoadingException {
+ return readFlashlingo(flashlingoStorage.getFlashlingoFilePath());
+ }
+
+ @Override
+ public Optional readFlashlingo(Path filePath) throws DataLoadingException {
+ logger.fine("Attempting to read data from file: " + filePath);
+ return flashlingoStorage.readFlashlingo(filePath);
+ }
+
+ @Override
+ public void saveFlashlingo(ReadOnlyFlashlingo flashlingo) throws IOException {
+ saveFlashlingo(flashlingo, flashlingoStorage.getFlashlingoFilePath());
+ }
+
+ @Override
+ public void saveFlashlingo(ReadOnlyFlashlingo flashlingo, Path filePath) throws IOException {
+ logger.fine("Attempting to write to data file: " + filePath);
+ flashlingoStorage.saveFlashlingo(flashlingo, filePath);
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/flashlingo/storage/UserPrefsStorage.java
similarity index 69%
rename from src/main/java/seedu/address/storage/UserPrefsStorage.java
rename to src/main/java/seedu/flashlingo/storage/UserPrefsStorage.java
index e94ca422ea8..73d4a69c079 100644
--- a/src/main/java/seedu/address/storage/UserPrefsStorage.java
+++ b/src/main/java/seedu/flashlingo/storage/UserPrefsStorage.java
@@ -1,15 +1,16 @@
-package seedu.address.storage;
+//@@author
+package seedu.flashlingo.storage;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.ReadOnlyUserPrefs;
-import seedu.address.model.UserPrefs;
+import seedu.flashlingo.commons.exceptions.DataLoadingException;
+import seedu.flashlingo.model.ReadOnlyUserPrefs;
+import seedu.flashlingo.model.UserPrefs;
/**
- * Represents a storage for {@link seedu.address.model.UserPrefs}.
+ * Represents a storage for {@link UserPrefs}.
*/
public interface UserPrefsStorage {
@@ -27,7 +28,7 @@ public interface UserPrefsStorage {
Optional readUserPrefs() throws DataLoadingException;
/**
- * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage.
+ * Saves the given {@link 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/seedu/flashlingo/ui/CommandBox.java
similarity index 88%
rename from src/main/java/seedu/address/ui/CommandBox.java
rename to src/main/java/seedu/flashlingo/ui/CommandBox.java
index 9e75478664b..730d97ad048 100644
--- a/src/main/java/seedu/address/ui/CommandBox.java
+++ b/src/main/java/seedu/flashlingo/ui/CommandBox.java
@@ -1,12 +1,13 @@
-package seedu.address.ui;
+package seedu.flashlingo.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 seedu.flashlingo.logic.Logic;
+import seedu.flashlingo.logic.commands.CommandResult;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
/**
* The UI component that is responsible for receiving user command inputs.
@@ -77,7 +78,7 @@ public interface CommandExecutor {
/**
* Executes the command and returns the result.
*
- * @see seedu.address.logic.Logic#execute(String)
+ * @see Logic#execute(String)
*/
CommandResult execute(String commandText) throws CommandException, ParseException;
}
diff --git a/src/main/java/seedu/flashlingo/ui/FlashcardBox.java b/src/main/java/seedu/flashlingo/ui/FlashcardBox.java
new file mode 100644
index 00000000000..6e19115eb6c
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/ui/FlashcardBox.java
@@ -0,0 +1,114 @@
+//@@author itsNatTan
+package seedu.flashlingo.ui;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.logic.session.SessionManager;
+import seedu.flashlingo.model.flashcard.FlashCard;
+
+/**
+ * An UI component that displays information of a {@code FlashCard}.
+ * @author Nathanael M. Tan
+ * @version 1.2
+ * @since 1.2
+ */
+public class FlashcardBox extends UiPart {
+
+ private static final String FXML = "FlashcardBox.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 FlashCard flashCard;
+
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label id;
+ @FXML
+ private Label original;
+ @FXML
+ private Label translation;
+ @FXML
+ private Label level;
+
+ @FXML
+ private Label lang;
+
+ @FXML
+ private Button reveal;
+
+ private MainWindow mw;
+ private int index;
+
+ /**
+ * Creates a {@code FlashCard code} with the given {@code FlashCard} and index to display.
+ */
+ public FlashcardBox(FlashCard fc, int displayedIndex, MainWindow mw) {
+ super(FXML);
+ // Ensure that FlashCard with buttons is only created when in review session
+ //@@author itsNatTan
+ assert(SessionManager.getInstance().isReviewSession());
+ //@@author itsNatTan
+ this.flashCard = fc;
+ this.mw = mw;
+ this.index = displayedIndex;
+ original.setWrapText(true);
+ translation.setWrapText(true);
+ id.setText(displayedIndex + ") ");
+ original.setText(fc.getOriginalWord().getWord() + ": ");
+ if (fc.getIsRevealed()) {
+ translation.setText(flashCard.getTranslatedWord().getWord());
+ reveal.setText(" Hide ");
+ } else {
+ translation.setText("");
+ reveal.setText("Reveal");
+ }
+ level.setText("Current Level: " + fc.getProficiencyLevel().toString());
+ }
+
+ /**
+ * Handles success when user presses "Yes" button
+ */
+ @FXML
+ public void success() throws CommandException, ParseException {
+ this.mw.executeCommand("yes");
+ level.setText("Proficiency Level: " + flashCard.getProficiencyLevel().getLevel());
+ }
+
+ /**
+ * Handles failure when user presses "No" button
+ */
+ @FXML
+ public void failure() throws CommandException, ParseException {
+ this.mw.executeCommand("no");
+ level.setText("Proficiency Level: " + flashCard.getProficiencyLevel().getLevel());
+ }
+
+ /**
+ * Reveals translation and changes text in box to Hide if it is not displayed
+ * Hides translation and changes text to reveal if it is displayed
+ */
+ @FXML
+ public void toggleReveal() throws CommandException, ParseException {
+ if (flashCard.getIsRevealed()) {
+ translation.setText("");
+ this.mw.executeCommand("reveal " + index);
+ reveal.setText("Reveal");
+ } else {
+ translation.setText(flashCard.getTranslatedWord().toString());
+ this.mw.executeCommand("reveal " + index);
+ reveal.setText(" Hide ");
+ }
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/ui/FlashcardBoxNoButton.java b/src/main/java/seedu/flashlingo/ui/FlashcardBoxNoButton.java
new file mode 100644
index 00000000000..f82f2c41b0e
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/ui/FlashcardBoxNoButton.java
@@ -0,0 +1,92 @@
+//@@author itsNatTan
+package seedu.flashlingo.ui;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.logic.session.SessionManager;
+import seedu.flashlingo.model.flashcard.FlashCard;
+
+/**
+ * An UI component that displays information of a {@code FlashCard}.
+ * @author Nathanael M. Tan
+ * @version 1.2
+ * @since 1.2
+ */
+public class FlashcardBoxNoButton extends UiPart {
+
+ private static final String FXML = "FlashcardBoxNoButton.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 FlashCard flashCard;
+
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label id;
+ @FXML
+ private Label original;
+ @FXML
+ private Label translation;
+ @FXML
+ private Label level;
+ @FXML
+ private Button reveal;
+
+ @FXML
+ private Label lang;
+
+ private MainWindow mw;
+ private int index;
+
+ /**
+ * Creates a {@code FlashCard code} with the given {@code FlashCard} and index to display.
+ */
+ public FlashcardBoxNoButton(FlashCard fc, int displayedIndex, MainWindow mw) {
+ super(FXML);
+ this.flashCard = fc;
+ this.mw = mw;
+ this.index = displayedIndex;
+ original.setWrapText(true);
+ translation.setWrapText(true);
+ assert(!SessionManager.getInstance().isReviewSession());
+ id.setText(displayedIndex + ") ");
+ original.setText(fc.getOriginalWord().getWord() + ": ");
+ if (fc.getIsRevealed()) {
+ translation.setText(flashCard.getTranslatedWord().getWord());
+ reveal.setText(" Hide ");
+ } else {
+ translation.setText("");
+ reveal.setText("Reveal");
+ }
+ level.setText("Current Level: " + fc.getProficiencyLevel().toString());
+ }
+
+ /**
+ * Reveals translation and changes text in box to Hide if it is not displayed
+ * Hides translation and changes text to reveal if it is displayed
+ */
+ @FXML
+ public void toggleReveal() throws CommandException, ParseException {
+ if (flashCard.getIsRevealed()) {
+ translation.setText("");
+ this.mw.executeCommand("reveal " + index);
+ reveal.setText("Reveal");
+ } else {
+ translation.setText(flashCard.getTranslatedWord().toString());
+ this.mw.executeCommand("reveal " + index);
+ reveal.setText(" Hide ");
+ }
+ }
+}
diff --git a/src/main/java/seedu/flashlingo/ui/FlashcardListPanel.java b/src/main/java/seedu/flashlingo/ui/FlashcardListPanel.java
new file mode 100644
index 00000000000..68517ee77b9
--- /dev/null
+++ b/src/main/java/seedu/flashlingo/ui/FlashcardListPanel.java
@@ -0,0 +1,61 @@
+//@@author itsNatTan
+package seedu.flashlingo.ui;
+
+import java.util.logging.Logger;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.layout.Region;
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.logic.session.SessionManager;
+import seedu.flashlingo.model.flashcard.FlashCard;
+
+
+/**
+ * Panel containing the list of flash cards.
+ */
+public class FlashcardListPanel extends UiPart {
+ private static final String FXML = "FlashcardListPanel.fxml";
+ private final Logger logger = LogsCenter.getLogger(FlashcardListPanel.class);
+
+ @FXML
+ private ListView flashcardListView;
+ private MainWindow mw;
+
+ /**
+ * Creates a {@code FlashCardsListPanel} with the given {@code ObservableList}.
+ */
+ public FlashcardListPanel(ObservableList flashcardList, MainWindow mw) {
+ super(FXML);
+ this.mw = mw;
+ flashcardListView.setItems(flashcardList);
+ flashcardListView.setCellFactory(listView -> new FlashCardListViewCell());
+ }
+
+ public void update() {
+ flashcardListView.setCellFactory(listView -> new FlashCardListViewCell());
+ }
+ /**
+ * Custom {@code ListCell} that displays the graphics of a {@code flash card} using a {@code FlashCard}.
+ */
+ class FlashCardListViewCell extends ListCell {
+ @Override
+ protected void updateItem(FlashCard fc, boolean empty) {
+ super.updateItem(fc, empty);
+
+ if (empty || fc == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ if (SessionManager.getInstance().isReviewSession()) {
+ setGraphic(new FlashcardBox(fc, getIndex() + 1, mw).getRoot());
+ } else {
+ setGraphic(new FlashcardBoxNoButton(fc, getIndex() + 1, mw).getRoot());
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/flashlingo/ui/HelpWindow.java
similarity index 92%
rename from src/main/java/seedu/address/ui/HelpWindow.java
rename to src/main/java/seedu/flashlingo/ui/HelpWindow.java
index 3f16b2fcf26..25db51061d8 100644
--- a/src/main/java/seedu/address/ui/HelpWindow.java
+++ b/src/main/java/seedu/flashlingo/ui/HelpWindow.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package seedu.flashlingo.ui;
import java.util.logging.Logger;
@@ -8,14 +8,14 @@
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.stage.Stage;
-import seedu.address.commons.core.LogsCenter;
+import seedu.flashlingo.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://ay2324s1-cs2103t-t11-4.github.io/tp/UserGuide.html";
public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL;
private static final Logger logger = LogsCenter.getLogger(HelpWindow.class);
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/flashlingo/ui/MainWindow.java
similarity index 64%
rename from src/main/java/seedu/address/ui/MainWindow.java
rename to src/main/java/seedu/flashlingo/ui/MainWindow.java
index 79e74ef37c0..e6ee296aeea 100644
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ b/src/main/java/seedu/flashlingo/ui/MainWindow.java
@@ -1,28 +1,32 @@
-package seedu.address.ui;
+package seedu.flashlingo.ui;
+import java.awt.Desktop;
+import java.net.URI;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
+import javafx.scene.Scene;
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 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 seedu.flashlingo.commons.core.GuiSettings;
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.logic.Logic;
+import seedu.flashlingo.logic.commands.CommandResult;
+import seedu.flashlingo.logic.commands.exceptions.CommandException;
+import seedu.flashlingo.logic.parser.exceptions.ParseException;
+import seedu.flashlingo.model.Model;
/**
* 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 THEME_FILE_PATH_PREFIX = "/view/";
private static final String FXML = "MainWindow.fxml";
private final Logger logger = LogsCenter.getLogger(getClass());
@@ -31,10 +35,11 @@ public class MainWindow extends UiPart {
private Logic logic;
// Independent Ui parts residing in this Ui container
- private PersonListPanel personListPanel;
+ private FlashcardListPanel flashcardListPanel;
private ResultDisplay resultDisplay;
private HelpWindow helpWindow;
-
+ @FXML
+ private Scene scene;
@FXML
private StackPane commandBoxPlaceholder;
@@ -42,7 +47,7 @@ public class MainWindow extends UiPart {
private MenuItem helpMenuItem;
@FXML
- private StackPane personListPanelPlaceholder;
+ private StackPane flashCardListPanelPlaceholder;
@FXML
private StackPane resultDisplayPlaceholder;
@@ -50,22 +55,27 @@ public class MainWindow extends UiPart {
@FXML
private StackPane statusbarPlaceholder;
+ private Model model;
+
/**
* Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}.
*/
- public MainWindow(Stage primaryStage, Logic logic) {
+ public MainWindow(Stage primaryStage, Logic logic, Model model) {
super(FXML, primaryStage);
// Set dependencies
this.primaryStage = primaryStage;
this.logic = logic;
+ this.model = model;
// Configure the UI
setWindowDefaultSize(logic.getGuiSettings());
+ setColorTheme(logic.getTheme());
setAccelerators();
helpWindow = new HelpWindow();
+ primaryStage.setScene(scene);
}
public Stage getPrimaryStage() {
@@ -110,13 +120,13 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) {
* Fills up all the placeholders of this window.
*/
void fillInnerParts() {
- personListPanel = new PersonListPanel(logic.getFilteredPersonList());
- personListPanelPlaceholder.getChildren().add(personListPanel.getRoot());
+ flashcardListPanel = new FlashcardListPanel(logic.getFilteredFlashCardList(), this);
+ flashCardListPanelPlaceholder.getChildren().add(flashcardListPanel.getRoot());
resultDisplay = new ResultDisplay();
resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot());
- StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath());
+ StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getFlashlingoFilePath());
statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot());
CommandBox commandBox = new CommandBox(this::executeCommand);
@@ -135,18 +145,32 @@ private void setWindowDefaultSize(GuiSettings guiSettings) {
}
}
+ //@@author itsNatTan
/**
- * Opens the help window or focuses on it if it's already opened.
+ * Attempts to open the UserGuide in the default browser.
+ * Upon failure, pop up a HelpWindow with the URL shown, or focuses on it if it is already open
*/
@FXML
public void handleHelp() {
- if (!helpWindow.isShowing()) {
- helpWindow.show();
+ if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
+ try {
+ Desktop.getDesktop().browse(new URI("https://ay2324s1-cs2103t-t11-4.github.io/tp/UserGuide.html"));
+ } catch (Exception e) {
+ if (!helpWindow.isShowing()) {
+ helpWindow.show();
+ } else {
+ helpWindow.focus();
+ }
+ }
} else {
- helpWindow.focus();
+ if (!helpWindow.isShowing()) {
+ helpWindow.show();
+ } else {
+ helpWindow.focus();
+ }
}
}
-
+ //@@author
void show() {
primaryStage.show();
}
@@ -163,20 +187,15 @@ private void handleExit() {
primaryStage.hide();
}
- public PersonListPanel getPersonListPanel() {
- return personListPanel;
- }
-
/**
* Executes the command and returns the result.
*
- * @see seedu.address.logic.Logic#execute(String)
+ * @see Logic#execute(String)
*/
- private CommandResult executeCommand(String commandText) throws CommandException, ParseException {
+ public CommandResult 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();
@@ -186,6 +205,13 @@ private CommandResult executeCommand(String commandText) throws CommandException
handleExit();
}
+ if (commandResult.isSwitchTheme()) {
+ setColorTheme(logic.getTheme());
+ commandResult = new CommandResult(commandResult.getFeedbackToUser(), false, false, false);
+ }
+
+ flashcardListPanel.update();
+ resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());
return commandResult;
} catch (CommandException | ParseException e) {
logger.info("An error occurred while executing command: " + commandText);
@@ -193,4 +219,20 @@ private CommandResult executeCommand(String commandText) throws CommandException
throw e;
}
}
+
+ private void setColorTheme(String theme) {
+ if (theme.equals("Default")) {
+ switchTheme("Light");
+ } else {
+ switchTheme("Dark");
+ }
+ }
+
+ private void switchTheme(String theme) {
+ scene.getStylesheets().clear();
+ String themeFilePath = THEME_FILE_PATH_PREFIX + theme + "Theme.css";
+ String extensionsFilePath = THEME_FILE_PATH_PREFIX + "Extensions.css";
+ scene.getStylesheets().add(getClass().getResource(themeFilePath).toExternalForm());
+ scene.getStylesheets().add(getClass().getResource(extensionsFilePath).toExternalForm());
+ }
}
diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/flashlingo/ui/ResultDisplay.java
similarity index 95%
rename from src/main/java/seedu/address/ui/ResultDisplay.java
rename to src/main/java/seedu/flashlingo/ui/ResultDisplay.java
index 7d98e84eedf..6740e5783e3 100644
--- a/src/main/java/seedu/address/ui/ResultDisplay.java
+++ b/src/main/java/seedu/flashlingo/ui/ResultDisplay.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package seedu.flashlingo.ui;
import static java.util.Objects.requireNonNull;
diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/flashlingo/ui/StatusBarFooter.java
similarity index 95%
rename from src/main/java/seedu/address/ui/StatusBarFooter.java
rename to src/main/java/seedu/flashlingo/ui/StatusBarFooter.java
index b577f829423..36ee8b92949 100644
--- a/src/main/java/seedu/address/ui/StatusBarFooter.java
+++ b/src/main/java/seedu/flashlingo/ui/StatusBarFooter.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package seedu.flashlingo.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/seedu/flashlingo/ui/Ui.java
similarity index 84%
rename from src/main/java/seedu/address/ui/Ui.java
rename to src/main/java/seedu/flashlingo/ui/Ui.java
index 17aa0b494fe..5aad9e5d21d 100644
--- a/src/main/java/seedu/address/ui/Ui.java
+++ b/src/main/java/seedu/flashlingo/ui/Ui.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package seedu.flashlingo.ui;
import javafx.stage.Stage;
diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/flashlingo/ui/UiManager.java
similarity index 82%
rename from src/main/java/seedu/address/ui/UiManager.java
rename to src/main/java/seedu/flashlingo/ui/UiManager.java
index fdf024138bc..e11f3cf9f13 100644
--- a/src/main/java/seedu/address/ui/UiManager.java
+++ b/src/main/java/seedu/flashlingo/ui/UiManager.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package seedu.flashlingo.ui;
import java.util.logging.Logger;
@@ -7,10 +7,11 @@
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 seedu.flashlingo.MainApp;
+import seedu.flashlingo.commons.core.LogsCenter;
+import seedu.flashlingo.commons.util.StringUtil;
+import seedu.flashlingo.logic.Logic;
+import seedu.flashlingo.model.Model;
/**
* The manager of the UI component.
@@ -20,16 +21,18 @@ 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 static final String ICON_APPLICATION = "/images/flash_lingo.png";
private Logic logic;
+ private Model model;
private MainWindow mainWindow;
/**
* Creates a {@code UiManager} with the given {@code Logic}.
*/
- public UiManager(Logic logic) {
+ public UiManager(Logic logic, Model model) {
this.logic = logic;
+ this.model = model;
}
@Override
@@ -40,7 +43,7 @@ public void start(Stage primaryStage) {
primaryStage.getIcons().add(getImage(ICON_APPLICATION));
try {
- mainWindow = new MainWindow(primaryStage, logic);
+ mainWindow = new MainWindow(primaryStage, logic, model);
mainWindow.show(); //This should be called before creating other UI parts
mainWindow.fillInnerParts();
@@ -65,7 +68,7 @@ void showAlertDialogAndWait(Alert.AlertType type, String title, String headerTex
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.getDialogPane().getStylesheets().add("view/LightTheme.css");
alert.initOwner(owner);
alert.setTitle(title);
alert.setHeaderText(headerText);
diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/flashlingo/ui/UiPart.java
similarity index 97%
rename from src/main/java/seedu/address/ui/UiPart.java
rename to src/main/java/seedu/flashlingo/ui/UiPart.java
index fc820e01a9c..403c79f8969 100644
--- a/src/main/java/seedu/address/ui/UiPart.java
+++ b/src/main/java/seedu/flashlingo/ui/UiPart.java
@@ -1,4 +1,4 @@
-package seedu.address.ui;
+package seedu.flashlingo.ui;
import static java.util.Objects.requireNonNull;
@@ -6,7 +6,7 @@
import java.net.URL;
import javafx.fxml.FXMLLoader;
-import seedu.address.MainApp;
+import seedu.flashlingo.MainApp;
/**
* Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc.
diff --git a/src/main/resources/images/flash_lingo.png b/src/main/resources/images/flash_lingo.png
new file mode 100644
index 00000000000..17686a3c9fb
Binary files /dev/null and b/src/main/resources/images/flash_lingo.png differ
diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml
index 124283a392e..779bc3bb556 100644
--- a/src/main/resources/view/CommandBox.fxml
+++ b/src/main/resources/view/CommandBox.fxml
@@ -3,7 +3,7 @@
-
-
+
+
diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css
index 36e6b001cd8..366b1de647d 100644
--- a/src/main/resources/view/DarkTheme.css
+++ b/src/main/resources/view/DarkTheme.css
@@ -3,30 +3,39 @@
background-color: #383838; /* Used in the default.html file */
}
+.list-cell:empty {
+ /* Empty cells will not have alternating colours */
+ -fx-background: #383838;
+}
+
+.root {
+ -fx-background-color: #383838;
+}
+
.label {
-fx-font-size: 11pt;
- -fx-font-family: "Segoe UI Semibold";
+ -fx-font-family: "Courier New";
-fx-text-fill: #555555;
-fx-opacity: 0.9;
}
.label-bright {
-fx-font-size: 11pt;
- -fx-font-family: "Segoe UI Semibold";
+ -fx-font-family: "Courier New";
-fx-text-fill: white;
-fx-opacity: 1;
}
.label-header {
-fx-font-size: 32pt;
- -fx-font-family: "Segoe UI Light";
+ -fx-font-family: "Courier New";
-fx-text-fill: white;
-fx-opacity: 1;
}
.text-field {
-fx-font-size: 12pt;
- -fx-font-family: "Segoe UI Semibold";
+ -fx-font-family: "Courier New";
}
.tab-pane {
@@ -66,7 +75,7 @@
.table-view .column-header .label {
-fx-font-size: 20pt;
- -fx-font-family: "Segoe UI Light";
+ -fx-font-family: "Courier New";
-fx-text-fill: white;
-fx-alignment: center-left;
-fx-opacity: 1;
@@ -121,13 +130,13 @@
}
.cell_big_label {
- -fx-font-family: "Segoe UI Semibold";
+ -fx-font-family: "Courier New";
-fx-font-size: 16px;
-fx-text-fill: #010504;
}
.cell_small_label {
- -fx-font-family: "Segoe UI";
+ -fx-font-family: "Courier New";
-fx-font-size: 13px;
-fx-text-fill: #010504;
}
@@ -148,7 +157,7 @@
.result-display {
-fx-background-color: transparent;
- -fx-font-family: "Segoe UI Light";
+ -fx-font-family: "Courier New";
-fx-font-size: 13pt;
-fx-text-fill: white;
}
@@ -158,7 +167,7 @@
}
.status-bar .label {
- -fx-font-family: "Segoe UI Light";
+ -fx-font-family: "Courier New";
-fx-text-fill: white;
-fx-padding: 4px;
-fx-pref-height: 30px;
@@ -198,7 +207,7 @@
.menu-bar .label {
-fx-font-size: 14pt;
- -fx-font-family: "Segoe UI Light";
+ -fx-font-family: "Courier New";
-fx-text-fill: white;
-fx-opacity: 0.9;
}
@@ -218,7 +227,7 @@
-fx-border-width: 2;
-fx-background-radius: 0;
-fx-background-color: #1d1d1d;
- -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
+ -fx-font-family: "Courier New", Helvetica, Arial, sans-serif;
-fx-font-size: 11pt;
-fx-text-fill: #d8d8d8;
-fx-background-insets: 0 0 0 0, 0, 1, 2;
@@ -323,7 +332,7 @@
-fx-border-color: #383838 #383838 #ffffff #383838;
-fx-border-insets: 0;
-fx-border-width: 1;
- -fx-font-family: "Segoe UI Light";
+ -fx-font-family: "Courier New";
-fx-font-size: 13pt;
-fx-text-fill: white;
}
diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css
index bfe82a85964..8a7d277d0fb 100644
--- a/src/main/resources/view/Extensions.css
+++ b/src/main/resources/view/Extensions.css
@@ -1,13 +1,7 @@
-
.error {
-fx-text-fill: #d06651 !important; /* The error class should always override the default text-fill style */
}
-.list-cell:empty {
- /* Empty cells will not have alternating colours */
- -fx-background: #383838;
-}
-
.tag-selector {
-fx-border-width: 1;
-fx-border-color: white;
@@ -18,3 +12,4 @@
.tooltip-text {
-fx-text-fill: white;
}
+
diff --git a/src/main/resources/view/FlashcardBox.fxml b/src/main/resources/view/FlashcardBox.fxml
new file mode 100644
index 00000000000..7b71bdca03f
--- /dev/null
+++ b/src/main/resources/view/FlashcardBox.fxml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/FlashcardBoxNoButton.fxml b/src/main/resources/view/FlashcardBoxNoButton.fxml
new file mode 100644
index 00000000000..c7d6ff5630b
--- /dev/null
+++ b/src/main/resources/view/FlashcardBoxNoButton.fxml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/FlashcardListCard.fxml b/src/main/resources/view/FlashcardListCard.fxml
new file mode 100644
index 00000000000..e6f4bf06d43
--- /dev/null
+++ b/src/main/resources/view/FlashcardListCard.fxml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/FlashcardListPanel.fxml b/src/main/resources/view/FlashcardListPanel.fxml
new file mode 100644
index 00000000000..80077c09b14
--- /dev/null
+++ b/src/main/resources/view/FlashcardListPanel.fxml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/LightTheme.css b/src/main/resources/view/LightTheme.css
new file mode 100644
index 00000000000..9c48479be2d
--- /dev/null
+++ b/src/main/resources/view/LightTheme.css
@@ -0,0 +1,268 @@
+.background {
+ -fx-background-color: #f0f0f0;
+}
+
+.list-cell:empty {
+ /* Empty cells will not have alternating colours */
+ -fx-background: #f0f0f0;
+}
+
+.root {
+ -fx-font-family: "Courier New";
+ -fx-background-color: #f0f0f0;
+}
+
+.label {
+ -fx-font-size: 13pt;
+ -fx-text-fill: #555555;
+ -fx-opacity: 0.9;
+}
+
+.label-bright {
+ -fx-font-size: 13pt;
+ -fx-text-fill: black;
+ -fx-opacity: 1;
+}
+
+.label-header {
+ -fx-font-size: 32pt;
+ -fx-text-fill: black;
+ -fx-opacity: 1;
+}
+
+.text-field {
+ -fx-font-size: 13pt;
+}
+
+.tab-pane {
+ -fx-padding: 0 0 0 1;
+}
+
+.tab-pane .tab-header-area {
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
+}
+
+.table-view {
+ -fx-base: #f0f0f0;
+ -fx-control-inner-background: #f0f0f0;
+ -fx-background-color: #f0f0f0;
+ -fx-table-cell-border-color: transparent;
+ -fx-table-header-border-color: transparent;
+ -fx-padding: 5;
+}
+
+.table-view .column-header-background {
+ -fx-background-color: #f0f0f0;
+}
+
+.table-view .column-header, .table-view .filler {
+ -fx-size: 35;
+ -fx-border-width: 0 0 1 0;
+ -fx-background-color: #f0f0f0;
+ -fx-border-color:
+ transparent
+ transparent
+ derive(-fx-base, 80%)
+ transparent;
+ -fx-border-insets: 0 10 1 0;
+}
+
+.table-view .column-header .label {
+ -fx-font-size: 20pt;
+ -fx-text-fill: black;
+ -fx-alignment: center-left;
+ -fx-opacity: 1;
+}
+
+.table-view:focused .table-row-cell:filled:focused:selected {
+ -fx-background-color: -fx-focus-color;
+}
+
+.split-pane:horizontal .split-pane-divider {
+ -fx-background-color: #f0f0f0;
+ -fx-border-color: transparent transparent transparent #4d4d4d;
+}
+
+.split-pane {
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: #f0f0f0;
+}
+
+.list-view {
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+ -fx-background-color: #f0f0f0;
+}
+
+.list-cell {
+ -fx-label-padding: 0 0 0 0;
+ -fx-graphic-text-gap : 0;
+ -fx-padding: 0 0 0 0;
+}
+
+.list-cell:filled:even {
+ -fx-background-color: #b5c4b1;
+}
+
+.list-cell:filled:odd {
+ -fx-background-color: #96a48b;
+}
+
+.list-cell:filled:selected {
+ -fx-background-color: #7b8b6f;
+}
+
+.list-cell:filled:selected #cardPane {
+ -fx-border-color: #f0f0f0;
+ -fx-border-width: 1;
+}
+
+.list-cell .label {
+ -fx-text-fill: black;
+}
+
+.cell_big_label {
+ -fx-font-size: 13pt;
+ -fx-font-weight: bold;
+ -fx-text-fill: black;
+}
+
+.cell_small_label {
+ -fx-font-size: 11pt;
+ -fx-text-fill: black;
+}
+
+.stack-pane {
+ -fx-background-color: #f0f0f0;
+}
+
+.pane-with-border{
+ -fx-background-color: #f0f0f0;
+}
+
+.status-bar {
+ -fx-background-color: #f0f0f0;
+}
+
+.command-box {
+ -fx-background-color: #f0f0f0;
+ -fx-border-color: #f0f0f0;
+ -fx-font-size: 13pt;
+ -fx-font-weight: bold;
+}
+
+.result-display {
+ -fx-background-color: #f0f0f0;
+ -fx-border-color: #7b8b6f #f0f0f0 #f0f0f0 #f0f0f0;
+ -fx-border-width: 4;
+ -fx-font-weight: bold;
+ -fx-font-size: 13pt;
+ -fx-text-fill: #d3ad97;
+}
+
+.result-display .label {
+ -fx-background-color: #f0f0f0;
+ -fx-text-fill: black !important;
+}
+
+.result-display .content {
+ -fx-background-color: #f0f0f0;
+}
+
+.status-bar .label {
+ -fx-text-fill: black;
+ -fx-padding: 4px;
+ -fx-pref-height: 30px;
+}
+
+.status-bar-with-border {
+ -fx-background-color: #e0e0e0;
+ -fx-border-color: #d0d0d0;
+ -fx-border-width: 1px;
+}
+
+.status-bar-with-border .label {
+ -fx-text-fill: black;
+}
+
+.grid-pane {
+ -fx-background-color: #e0e0e0;
+ -fx-border-color: #d0d0d0;
+ -fx-border-width: 1px;
+}
+
+.grid-pane .stack-pane {
+ -fx-background-color: #f0f0f0;
+}
+
+.context-menu {
+ -fx-background-color: #f0f0f0;
+}
+
+.context-menu .label {
+ -fx-text-fill: black;
+}
+
+.menu-bar {
+ -fx-background-color: #f0f0f0;
+}
+
+.menu-bar .label {
+ -fx-font-size: 13pt;
+ -fx-text-fill: black;
+ -fx-opacity: 0.9;
+}
+
+.menu .left-container {
+ -fx-background-color: #f0f0f0;
+}
+
+.button {
+ -fx-padding: 4 20 4 20;
+ -fx-border-width: 2;
+ -fx-background-radius: 20;
+ -fx-background-color: white;
+ -fx-background-radius: 3px;
+ -fx-font-size: 11pt;
+}
+
+.button:hover {
+ -fx-background-color: #f1d3c2;
+ -fx-font-size: 11pt;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.button:pressed {
+ -fx-background-color: #ebc1a8;
+ -fx-font-size: 11pt;
+ -fx-font-weight: bold;
+ -fx-text-fill: white;
+}
+
+.menu-item {
+ -fx-background-color: #f0f0f0;
+}
+
+.menu-item:hover {
+ -fx-background-color: #f1d3c2;
+}
+
+.menu-item:pressed {
+ -fx-background-color: #ebc1a8;
+}
+
+.menu {
+ -fx-background-color: #f0f0f0;
+}
+
+.menu:hover {
+ -fx-background-color: #f1d3c2;
+}
+
+.menu:pressed {
+ -fx-background-color: #ebc1a8;
+}
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
index 7778f666a0a..67056bd515f 100644
--- a/src/main/resources/view/MainWindow.fxml
+++ b/src/main/resources/view/MainWindow.fxml
@@ -6,51 +6,51 @@
-
-
+
+
+ title="Flashlingo" minWidth="450" minHeight="600" onCloseRequest="#handleExit">
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
-
+
diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml
deleted file mode 100644
index f5e812e25e6..00000000000
--- a/src/main/resources/view/PersonListCard.fxml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml
index 01b691792a9..611a773592b 100644
--- a/src/main/resources/view/ResultDisplay.fxml
+++ b/src/main/resources/view/ResultDisplay.fxml
@@ -4,6 +4,6 @@
-
+ xmlns:fx="http://javafx.com/fxml/1">
+
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
deleted file mode 100644
index 6a4d2b7181c..00000000000
--- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "persons": [ {
- "name": "Valid Person",
- "phone": "9482424",
- "email": "hans@example.com",
- "address": "4th street"
- }, {
- "name": "Person With Invalid Phone Field",
- "phone": "948asdf2424",
- "email": "hans@example.com",
- "address": "4th street"
- } ]
-}
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
deleted file mode 100644
index ccd21f7d1a9..00000000000
--- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "persons": [ {
- "name": "Person with invalid name field: Ha!ns Mu@ster",
- "phone": "9482424",
- "email": "hans@example.com",
- "address": "4th street"
- } ]
-}
diff --git a/src/test/data/JsonFlashlingoStorageTest/invalidAndValidFlashCardFlashlingo.json b/src/test/data/JsonFlashlingoStorageTest/invalidAndValidFlashCardFlashlingo.json
new file mode 100644
index 00000000000..63de675d146
--- /dev/null
+++ b/src/test/data/JsonFlashlingoStorageTest/invalidAndValidFlashCardFlashlingo.json
@@ -0,0 +1,17 @@
+{
+ "flashcards" : [ {
+ "originalWord" : "hi",
+ "originalWordLanguage" : "eng",
+ "translatedWord" : "konnichiwa",
+ "translatedWordLanguage" : "jap",
+ "whenToReview" : "2023-10-25T17:34:24Z",
+ "level" : 2
+ },{
+ "originalWord" : "hi",
+ "originalWordLanguage" : "eng",
+ "translatedWord" : "konnichiwa",
+ "translatedWordLanguage" : "jap",
+ "whenToReview" : "2023-10-25T17:34:24Z",
+ "level" : -1
+ }]
+}
diff --git a/src/test/data/JsonFlashlingoStorageTest/invalidFlashCardFlashlingo.json b/src/test/data/JsonFlashlingoStorageTest/invalidFlashCardFlashlingo.json
new file mode 100644
index 00000000000..e88299e96b7
--- /dev/null
+++ b/src/test/data/JsonFlashlingoStorageTest/invalidFlashCardFlashlingo.json
@@ -0,0 +1,10 @@
+{
+ "flashcards" : [ {
+ "originalWord" : "hi",
+ "originalWordLanguage" : "eng",
+ "translatedWord" : "konnichiwa",
+ "translatedWordLanguage" : "jap",
+ "whenToReview" : "2023-10-25T17:34:24Z",
+ "level" : -1
+ }]
+}
diff --git a/src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json b/src/test/data/JsonFlashlingoStorageTest/notJsonFormatFlashlingo.json
similarity index 100%
rename from src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json
rename to src/test/data/JsonFlashlingoStorageTest/notJsonFormatFlashlingo.json
diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
deleted file mode 100644
index a7427fe7aa2..00000000000
--- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "persons": [ {
- "name": "Alice Pauline",
- "phone": "94351253",
- "email": "alice@example.com",
- "address": "123, Jurong West Ave 6, #08-111",
- "tags": [ "friends" ]
- }, {
- "name": "Alice Pauline",
- "phone": "94351253",
- "email": "pauline@example.com",
- "address": "4th street"
- } ]
-}
diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
deleted file mode 100644
index ad3f135ae42..00000000000
--- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "persons": [ {
- "name": "Hans Muster",
- "phone": "9482424",
- "email": "invalid@email!3e",
- "address": "4th street"
- } ]
-}
diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
deleted file mode 100644
index 72262099d35..00000000000
--- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()",
- "persons" : [ {
- "name" : "Alice Pauline",
- "phone" : "94351253",
- "email" : "alice@example.com",
- "address" : "123, Jurong West Ave 6, #08-111",
- "tags" : [ "friends" ]
- }, {
- "name" : "Benson Meier",
- "phone" : "98765432",
- "email" : "johnd@example.com",
- "address" : "311, Clementi Ave 2, #02-25",
- "tags" : [ "owesMoney", "friends" ]
- }, {
- "name" : "Carl Kurz",
- "phone" : "95352563",
- "email" : "heinz@example.com",
- "address" : "wall street",
- "tags" : [ ]
- }, {
- "name" : "Daniel Meier",
- "phone" : "87652533",
- "email" : "cornelia@example.com",
- "address" : "10th street",
- "tags" : [ "friends" ]
- }, {
- "name" : "Elle Meyer",
- "phone" : "9482224",
- "email" : "werner@example.com",
- "address" : "michegan ave",
- "tags" : [ ]
- }, {
- "name" : "Fiona Kunz",
- "phone" : "9482427",
- "email" : "lydia@example.com",
- "address" : "little tokyo",
- "tags" : [ ]
- }, {
- "name" : "George Best",
- "phone" : "9482442",
- "email" : "anna@example.com",
- "address" : "4th street",
- "tags" : [ ]
- } ]
-}
diff --git a/src/test/data/JsonSerializableFlashlingoTest/duplicateFlashCardFlashlingo.json b/src/test/data/JsonSerializableFlashlingoTest/duplicateFlashCardFlashlingo.json
new file mode 100644
index 00000000000..81655d4a541
--- /dev/null
+++ b/src/test/data/JsonSerializableFlashlingoTest/duplicateFlashCardFlashlingo.json
@@ -0,0 +1,17 @@
+{
+ "flashcards": [ {
+ "originalWord" : "hi",
+ "originalWordLanguage" : "eng",
+ "translatedWord" : "konnichiwa",
+ "translatedWordLanguage" : "jap",
+ "whenToReview" : "2023-10-25T17:34:24Z",
+ "level" : 1
+ }, {
+ "originalWord" : "hi",
+ "originalWordLanguage" : "eng",
+ "translatedWord" : "konnichiwa",
+ "translatedWordLanguage" : "jap",
+ "whenToReview" : "2023-10-25T17:34:24Z",
+ "level" : 1
+ } ]
+}
diff --git a/src/test/data/JsonSerializableFlashlingoTest/invalidFlashCardFlashlingo.json b/src/test/data/JsonSerializableFlashlingoTest/invalidFlashCardFlashlingo.json
new file mode 100644
index 00000000000..7b709022913
--- /dev/null
+++ b/src/test/data/JsonSerializableFlashlingoTest/invalidFlashCardFlashlingo.json
@@ -0,0 +1,10 @@
+{
+ "flashcards": [ {
+ "originalWord" : "hi",
+ "originalWordLanguage" : "eng",
+ "translatedWord" : "konnichiwa",
+ "translatedWordLanguage" : "jap",
+ "whenToReview" : "2023-10-25T17:34:24Z",
+ "level" : -1
+ } ]
+}
diff --git a/src/test/data/JsonSerializableFlashlingoTest/typicalFlashCardsFlashlingo.json b/src/test/data/JsonSerializableFlashlingoTest/typicalFlashCardsFlashlingo.json
new file mode 100644
index 00000000000..52ed59052a3
--- /dev/null
+++ b/src/test/data/JsonSerializableFlashlingoTest/typicalFlashCardsFlashlingo.json
@@ -0,0 +1,53 @@
+{
+ "_comment": "Flashlingo save file which contains the same FlashCard values as in TypicalFlashCards#getTypicalFlashlingo()",
+ "flashcards" : [ {
+ "originalWord" : "Alice Pauline",
+ "originalWordLanguage" : "English",
+ "translatedWord" : "爱丽丝·宝琳",
+ "translatedWordLanguage" : "Mandarin",
+ "whenToReview" : "2023-12-17T00:00:00Z",
+ "level" : 1
+ }, {
+ "originalWord" : "Benson Meier",
+ "originalWordLanguage" : "English",
+ "translatedWord" : "本森·梅尔",
+ "translatedWordLanguage" : "Mandarin",
+ "whenToReview" : "2023-12-17T00:00:00Z",
+ "level" : 1
+ }, {
+ "originalWord" : "Carl Kurz",
+ "originalWordLanguage" : "English",
+ "translatedWord" : "卡尔·库尔兹",
+ "translatedWordLanguage" : "Mandarin",
+ "whenToReview" : "2023-12-17T00:00:00Z",
+ "level" : 1
+ }, {
+ "originalWord" : "Daniel Meier",
+ "originalWordLanguage" : "English",
+ "translatedWord" : "丹尼尔·梅尔",
+ "translatedWordLanguage" : "Mandarin",
+ "whenToReview" : "2023-12-17T00:00:00Z",
+ "level" : 1
+ }, {
+ "originalWord" : "Elle Meyer",
+ "originalWordLanguage" : "English",
+ "translatedWord" : "艾爾·邁耶",
+ "translatedWordLanguage" : "Traditional Chinese",
+ "whenToReview" : "2023-12-17T00:00:00Z",
+ "level" : 1
+ }, {
+ "originalWord" : "Fiona Kunz",
+ "originalWordLanguage" : "English",
+ "translatedWord" : "菲奥娜昆兹",
+ "translatedWordLanguage" : "Chinese",
+ "whenToReview" : "2023-12-17T00:00:00Z",
+ "level" : 1
+ }, {
+ "originalWord" : "George Best",
+ "originalWordLanguage" : "English",
+ "translatedWord" : "乔治·贝斯特",
+ "translatedWordLanguage" : "",
+ "whenToReview" : "2023-01-01T00:00:00Z",
+ "level" : 1
+ } ]
+}
diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json
index b819bed900a..0f4edeede96 100644
--- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json
+++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json
@@ -7,5 +7,6 @@
"y" : 100
}
},
- "addressBookFilePath" : "addressbook.json"
+ "flashlingoFilePath" : "data/flashlingo.json",
+ "theme" : "Default"
}
diff --git a/src/test/data/LoadCommandTest/duplicateFlashCardFile.xlsx b/src/test/data/LoadCommandTest/duplicateFlashCardFile.xlsx
new file mode 100644
index 00000000000..6a1651e79de
Binary files /dev/null and b/src/test/data/LoadCommandTest/duplicateFlashCardFile.xlsx differ
diff --git a/src/test/data/LoadCommandTest/invalidFormatFile.xlsx b/src/test/data/LoadCommandTest/invalidFormatFile.xlsx
new file mode 100644
index 00000000000..0786ee53297
Binary files /dev/null and b/src/test/data/LoadCommandTest/invalidFormatFile.xlsx differ
diff --git a/src/test/data/LoadCommandTest/invalidTypeFile.json b/src/test/data/LoadCommandTest/invalidTypeFile.json
new file mode 100644
index 00000000000..63de675d146
--- /dev/null
+++ b/src/test/data/LoadCommandTest/invalidTypeFile.json
@@ -0,0 +1,17 @@
+{
+ "flashcards" : [ {
+ "originalWord" : "hi",
+ "originalWordLanguage" : "eng",
+ "translatedWord" : "konnichiwa",
+ "translatedWordLanguage" : "jap",
+ "whenToReview" : "2023-10-25T17:34:24Z",
+ "level" : 2
+ },{
+ "originalWord" : "hi",
+ "originalWordLanguage" : "eng",
+ "translatedWord" : "konnichiwa",
+ "translatedWordLanguage" : "jap",
+ "whenToReview" : "2023-10-25T17:34:24Z",
+ "level" : -1
+ }]
+}
diff --git a/src/test/data/LoadCommandTest/validFlashCardFile.xlsx b/src/test/data/LoadCommandTest/validFlashCardFile.xlsx
new file mode 100644
index 00000000000..935ebea5469
Binary files /dev/null and b/src/test/data/LoadCommandTest/validFlashCardFile.xlsx differ
diff --git a/src/test/data/sandbox/serialize.json b/src/test/data/sandbox/serialize.json
new file mode 100644
index 00000000000..9a4c0ec3908
--- /dev/null
+++ b/src/test/data/sandbox/serialize.json
@@ -0,0 +1,9 @@
+{
+ "name" : "This is a test class",
+ "listOfLocalDateTimes" : [ "-999999999-01-01T00:00:00", "+999999999-12-31T23:59:59.999999999", "0001-01-01T01:01:00" ],
+ "mapOfIntegerToString" : {
+ "1" : "One",
+ "2" : "Two",
+ "3" : "Three"
+ }
+}
diff --git a/src/test/java/seedu/address/AppParametersTest.java b/src/test/java/seedu/address/AppParametersTest.java
deleted file mode 100644
index 133cc008bce..00000000000
--- a/src/test/java/seedu/address/AppParametersTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package seedu.address;
-
-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 java.nio.file.Paths;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.junit.jupiter.api.Test;
-
-import javafx.application.Application;
-
-public class AppParametersTest {
-
- private final ParametersStub parametersStub = new ParametersStub();
- private final AppParameters expected = new AppParameters();
-
- @Test
- public void parse_validConfigPath_success() {
- parametersStub.namedParameters.put("config", "config.json");
- expected.setConfigPath(Paths.get("config.json"));
- assertEquals(expected, AppParameters.parse(parametersStub));
- }
-
- @Test
- public void parse_nullConfigPath_success() {
- parametersStub.namedParameters.put("config", null);
- assertEquals(expected, AppParameters.parse(parametersStub));
- }
-
- @Test
- public void parse_invalidConfigPath_success() {
- parametersStub.namedParameters.put("config", "a\0");
- expected.setConfigPath(null);
- assertEquals(expected, AppParameters.parse(parametersStub));
- }
-
- @Test
- public void toStringMethod() {
- AppParameters appParameters = new AppParameters();
- String expected = AppParameters.class.getCanonicalName() + "{configPath=" + appParameters.getConfigPath() + "}";
- assertEquals(expected, appParameters.toString());
- }
-
- @Test
- public void equals() {
- AppParameters appParameters = new AppParameters();
-
- // same values -> returns true
- assertTrue(appParameters.equals(new AppParameters()));
-
- // same object -> returns true
- assertTrue(appParameters.equals(appParameters));
-
- // null -> returns false
- assertFalse(appParameters.equals(null));
-
- // different types -> returns false
- assertFalse(appParameters.equals(5.0f));
-
- // different config path -> returns false
- AppParameters otherAppParameters = new AppParameters();
- otherAppParameters.setConfigPath(Paths.get("configPath"));
- assertFalse(appParameters.equals(otherAppParameters));
- }
-
- private static class ParametersStub extends Application.Parameters {
- private Map namedParameters = new HashMap<>();
-
- @Override
- public List getRaw() {
- throw new AssertionError("should not be called");
- }
-
- @Override
- public List getUnnamed() {
- throw new AssertionError("should not be called");
- }
-
- @Override
- public Map getNamed() {
- return Collections.unmodifiableMap(namedParameters);
- }
- }
-}
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
deleted file mode 100644
index 162a0c86031..00000000000
--- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package seedu.address.logic.commands;
-
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import seedu.address.logic.Messages;
-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;
-
-/**
- * Contains integration tests (interaction with the Model) for {@code AddCommand}.
- */
-public class AddCommandIntegrationTest {
-
- private Model model;
-
- @BeforeEach
- public void setUp() {
- model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- }
-
- @Test
- public void execute_newPerson_success() {
- Person validPerson = new PersonBuilder().build();
-
- Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
- expectedModel.addPerson(validPerson);
-
- assertCommandSuccess(new AddCommand(validPerson), model,
- String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
- expectedModel);
- }
-
- @Test
- public void execute_duplicatePerson_throwsCommandException() {
- Person personInList = model.getAddressBook().getPersonList().get(0);
- 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/seedu/address/logic/commands/AddCommandTest.java
deleted file mode 100644
index 90e8253f48e..00000000000
--- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java
+++ /dev/null
@@ -1,204 +0,0 @@
-package seedu.address.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 seedu.address.testutil.TypicalPersons.ALICE;
-
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.function.Predicate;
-
-import org.junit.jupiter.api.Test;
-
-import javafx.collections.ObservableList;
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.logic.Messages;
-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;
-
-public class AddCommandTest {
-
- @Test
- public void constructor_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new AddCommand(null));
- }
-
- @Test
- public void execute_personAcceptedByModel_addSuccessful() throws Exception {
- ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded();
- Person validPerson = new PersonBuilder().build();
-
- CommandResult commandResult = new AddCommand(validPerson).execute(modelStub);
-
- assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
- commandResult.getFeedbackToUser());
- assertEquals(Arrays.asList(validPerson), modelStub.personsAdded);
- }
-
- @Test
- public void execute_duplicatePerson_throwsCommandException() {
- Person validPerson = new PersonBuilder().build();
- AddCommand addCommand = new AddCommand(validPerson);
- ModelStub modelStub = new ModelStubWithPerson(validPerson);
-
- assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub));
- }
-
- @Test
- public void equals() {
- Person alice = new PersonBuilder().withName("Alice").build();
- Person bob = new PersonBuilder().withName("Bob").build();
- AddCommand addAliceCommand = new AddCommand(alice);
- AddCommand addBobCommand = new AddCommand(bob);
-
- // same object -> returns true
- assertTrue(addAliceCommand.equals(addAliceCommand));
-
- // same values -> returns true
- AddCommand addAliceCommandCopy = new AddCommand(alice);
- assertTrue(addAliceCommand.equals(addAliceCommandCopy));
-
- // different types -> returns false
- assertFalse(addAliceCommand.equals(1));
-
- // null -> returns false
- assertFalse(addAliceCommand.equals(null));
-
- // different person -> returns false
- assertFalse(addAliceCommand.equals(addBobCommand));
- }
-
- @Test
- public void toStringMethod() {
- AddCommand addCommand = new AddCommand(ALICE);
- String expected = AddCommand.class.getCanonicalName() + "{toAdd=" + ALICE + "}";
- assertEquals(expected, addCommand.toString());
- }
-
- /**
- * A default model stub that have all of the methods failing.
- */
- private class ModelStub implements Model {
- @Override
- public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public ReadOnlyUserPrefs getUserPrefs() {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public GuiSettings getGuiSettings() {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void setGuiSettings(GuiSettings guiSettings) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public Path getAddressBookFilePath() {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void setAddressBookFilePath(Path addressBookFilePath) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void addPerson(Person person) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void setAddressBook(ReadOnlyAddressBook newData) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public ReadOnlyAddressBook getAddressBook() {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public boolean hasPerson(Person person) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void deletePerson(Person target) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void setPerson(Person target, Person editedPerson) {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public ObservableList getFilteredPersonList() {
- throw new AssertionError("This method should not be called.");
- }
-
- @Override
- public void updateFilteredPersonList(Predicate predicate) {
- throw new AssertionError("This method should not be called.");
- }
- }
-
- /**
- * A Model stub that contains a single person.
- */
- private class ModelStubWithPerson extends ModelStub {
- private final Person person;
-
- ModelStubWithPerson(Person person) {
- requireNonNull(person);
- this.person = person;
- }
-
- @Override
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return this.person.isSamePerson(person);
- }
- }
-
- /**
- * A Model stub that always accept the person being added.
- */
- private class ModelStubAcceptingPersonAdded extends ModelStub {
- final ArrayList personsAdded = new ArrayList<>();
-
- @Override
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return personsAdded.stream().anyMatch(person::isSamePerson);
- }
-
- @Override
- public void addPerson(Person person) {
- requireNonNull(person);
- personsAdded.add(person);
- }
-
- @Override
- public ReadOnlyAddressBook getAddressBook() {
- return new AddressBook();
- }
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
deleted file mode 100644
index 80d9110c03a..00000000000
--- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package seedu.address.logic.commands;
-
-import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
-import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.model.AddressBook;
-import seedu.address.model.Model;
-import seedu.address.model.ModelManager;
-import seedu.address.model.UserPrefs;
-
-public class ClearCommandTest {
-
- @Test
- public void execute_emptyAddressBook_success() {
- Model model = new ModelManager();
- Model expectedModel = new ModelManager();
-
- assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel);
- }
-
- @Test
- public void execute_nonEmptyAddressBook_success() {
- Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- expectedModel.setAddressBook(new AddressBook());
-
- assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel);
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
deleted file mode 100644
index 643a1d08069..00000000000
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package seedu.address.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 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;
-
-/**
- * Contains helper methods for testing commands.
- */
-public class CommandTestUtil {
-
- public static final String VALID_NAME_AMY = "Amy Bee";
- public static final String VALID_NAME_BOB = "Bob Choo";
- public static final String VALID_PHONE_AMY = "11111111";
- public static final String VALID_PHONE_BOB = "22222222";
- public static final String VALID_EMAIL_AMY = "amy@example.com";
- public static final String VALID_EMAIL_BOB = "bob@example.com";
- public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1";
- public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3";
- public static final String VALID_TAG_HUSBAND = "husband";
- public static final String VALID_TAG_FRIEND = "friend";
-
- public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY;
- public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB;
- public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY;
- public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB;
- public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY;
- public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB;
- public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY;
- public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB;
- 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 PREAMBLE_WHITESPACE = "\t \r \n";
- public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";
-
- public static final EditCommand.EditPersonDescriptor DESC_AMY;
- public static final EditCommand.EditPersonDescriptor DESC_BOB;
-
- static {
- DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_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)
- .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
- }
-
- /**
- * Executes the given {@code command}, confirms that
- * - the returned {@link CommandResult} matches {@code expectedCommandResult}
- * - the {@code actualModel} matches {@code expectedModel}
- */
- public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult,
- Model expectedModel) {
- try {
- CommandResult result = command.execute(actualModel);
- assertEquals(expectedCommandResult, result);
- assertEquals(expectedModel, actualModel);
- } catch (CommandException ce) {
- throw new AssertionError("Execution of command should not fail.", ce);
- }
- }
-
- /**
- * 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,
- Model expectedModel) {
- CommandResult expectedCommandResult = new CommandResult(expectedMessage);
- assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel);
- }
-
- /**
- * 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
- */
- 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());
- List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList());
-
- assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel));
- 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.
- */
- 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])));
-
- assertEquals(1, model.getFilteredPersonList().size());
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
deleted file mode 100644
index 469dd97daa7..00000000000
--- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java
+++ /dev/null
@@ -1,184 +0,0 @@
-package seedu.address.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.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 org.junit.jupiter.api.Test;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.Messages;
-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;
-
-/**
- * Contains integration tests (interaction with the Model) and unit tests for EditCommand.
- */
-public class EditCommandTest {
-
- private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
-
- @Test
- public void execute_allFieldsSpecifiedUnfilteredList_success() {
- Person editedPerson = new PersonBuilder().build();
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build();
- EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor);
-
- String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
-
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
- expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
-
- assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
- }
-
- @Test
- public void execute_someFieldsSpecifiedUnfilteredList_success() {
- Index indexLastPerson = Index.fromOneBased(model.getFilteredPersonList().size());
- Person lastPerson = model.getFilteredPersonList().get(indexLastPerson.getZeroBased());
-
- PersonBuilder personInList = new PersonBuilder(lastPerson);
- Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB)
- .withTags(VALID_TAG_HUSBAND).build();
-
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB)
- .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build();
- EditCommand editCommand = new EditCommand(indexLastPerson, descriptor);
-
- String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
-
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
- expectedModel.setPerson(lastPerson, editedPerson);
-
- assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
- }
-
- @Test
- 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, Messages.format(editedPerson));
-
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
-
- assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
- }
-
- @Test
- 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();
- EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON,
- new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build());
-
- String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
-
- Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
- expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson);
-
- assertCommandSuccess(editCommand, model, expectedMessage, expectedModel);
- }
-
- @Test
- public void execute_duplicatePersonUnfilteredList_failure() {
- Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(firstPerson).build();
- EditCommand editCommand = new EditCommand(INDEX_SECOND_PERSON, descriptor);
-
- assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON);
- }
-
- @Test
- 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());
- EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON,
- new EditPersonDescriptorBuilder(personInList).build());
-
- assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON);
- }
-
- @Test
- public void execute_invalidPersonIndexUnfilteredList_failure() {
- Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
- 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
- */
- @Test
- public void execute_invalidPersonIndexFilteredList_failure() {
- showPersonAtIndex(model, INDEX_FIRST_PERSON);
- Index outOfBoundIndex = INDEX_SECOND_PERSON;
- // ensures that outOfBoundIndex is still in bounds of address book list
- assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
-
- EditCommand editCommand = new EditCommand(outOfBoundIndex,
- new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build());
-
- assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- @Test
- public void equals() {
- final EditCommand standardCommand = new EditCommand(INDEX_FIRST_PERSON, DESC_AMY);
-
- // same values -> returns true
- EditPersonDescriptor copyDescriptor = new EditPersonDescriptor(DESC_AMY);
- EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_PERSON, copyDescriptor);
- assertTrue(standardCommand.equals(commandWithSameValues));
-
- // same object -> returns true
- assertTrue(standardCommand.equals(standardCommand));
-
- // null -> returns false
- assertFalse(standardCommand.equals(null));
-
- // different types -> returns false
- assertFalse(standardCommand.equals(new ClearCommand()));
-
- // different index -> returns false
- assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY)));
-
- // different descriptor -> returns false
- assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_PERSON, DESC_BOB)));
- }
-
- @Test
- public void toStringMethod() {
- Index index = Index.fromOneBased(1);
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
- EditCommand editCommand = new EditCommand(index, editPersonDescriptor);
- String expected = EditCommand.class.getCanonicalName() + "{index=" + index + ", editPersonDescriptor="
- + editPersonDescriptor + "}";
- assertEquals(expected, editCommand.toString());
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
deleted file mode 100644
index b17c1f3d5c2..00000000000
--- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package seedu.address.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.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 org.junit.jupiter.api.Test;
-
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.testutil.EditPersonDescriptorBuilder;
-
-public class EditPersonDescriptorTest {
-
- @Test
- public void equals() {
- // same values -> returns true
- EditPersonDescriptor descriptorWithSameValues = new EditPersonDescriptor(DESC_AMY);
- assertTrue(DESC_AMY.equals(descriptorWithSameValues));
-
- // same object -> returns true
- assertTrue(DESC_AMY.equals(DESC_AMY));
-
- // null -> returns false
- assertFalse(DESC_AMY.equals(null));
-
- // different types -> returns false
- assertFalse(DESC_AMY.equals(5));
-
- // different values -> returns false
- assertFalse(DESC_AMY.equals(DESC_BOB));
-
- // different name -> returns false
- EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different phone -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different email -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different address -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different tags -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build();
- assertFalse(DESC_AMY.equals(editedAmy));
- }
-
- @Test
- public void toStringMethod() {
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
- String expected = EditPersonDescriptor.class.getCanonicalName() + "{name="
- + editPersonDescriptor.getName().orElse(null) + ", phone="
- + editPersonDescriptor.getPhone().orElse(null) + ", email="
- + editPersonDescriptor.getEmail().orElse(null) + ", address="
- + editPersonDescriptor.getAddress().orElse(null) + ", tags="
- + editPersonDescriptor.getTags().orElse(null) + "}";
- assertEquals(expected, editPersonDescriptor.toString());
- }
-}
diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
deleted file mode 100644
index b8b7dbba91a..00000000000
--- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package seedu.address.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.logic.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 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;
-
-/**
- * Contains integration tests (interaction with the Model) for {@code FindCommand}.
- */
-public class FindCommandTest {
- private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
-
- @Test
- public void equals() {
- NameContainsKeywordsPredicate firstPredicate =
- new NameContainsKeywordsPredicate(Collections.singletonList("first"));
- NameContainsKeywordsPredicate secondPredicate =
- new NameContainsKeywordsPredicate(Collections.singletonList("second"));
-
- FindCommand findFirstCommand = new FindCommand(firstPredicate);
- FindCommand findSecondCommand = new FindCommand(secondPredicate);
-
- // same object -> returns true
- assertTrue(findFirstCommand.equals(findFirstCommand));
-
- // same values -> returns true
- FindCommand findFirstCommandCopy = new FindCommand(firstPredicate);
- assertTrue(findFirstCommand.equals(findFirstCommandCopy));
-
- // different types -> returns false
- assertFalse(findFirstCommand.equals(1));
-
- // null -> returns false
- assertFalse(findFirstCommand.equals(null));
-
- // different person -> returns false
- assertFalse(findFirstCommand.equals(findSecondCommand));
- }
-
- @Test
- public void execute_zeroKeywords_noPersonFound() {
- String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0);
- NameContainsKeywordsPredicate predicate = preparePredicate(" ");
- FindCommand command = new FindCommand(predicate);
- expectedModel.updateFilteredPersonList(predicate);
- assertCommandSuccess(command, model, expectedMessage, expectedModel);
- assertEquals(Collections.emptyList(), model.getFilteredPersonList());
- }
-
- @Test
- public void execute_multipleKeywords_multiplePersonsFound() {
- String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3);
- NameContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz");
- FindCommand command = new FindCommand(predicate);
- expectedModel.updateFilteredPersonList(predicate);
- assertCommandSuccess(command, model, expectedMessage, expectedModel);
- assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList());
- }
-
- @Test
- public void toStringMethod() {
- NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Arrays.asList("keyword"));
- FindCommand findCommand = new FindCommand(predicate);
- String expected = FindCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
- assertEquals(expected, findCommand.toString());
- }
-
- /**
- * Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}.
- */
- private NameContainsKeywordsPredicate preparePredicate(String userInput) {
- return new NameContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+")));
- }
-}
diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCommandTest.java
deleted file mode 100644
index 435ff1f7275..00000000000
--- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package seedu.address.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 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;
-
-/**
- * Contains integration tests (interaction with the Model) and unit tests for ListCommand.
- */
-public class ListCommandTest {
-
- private Model model;
- private Model expectedModel;
-
- @BeforeEach
- public void setUp() {
- model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
- expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
- }
-
- @Test
- public void execute_listIsNotFiltered_showsSameList() {
- assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel);
- }
-
- @Test
- public void execute_listIsFiltered_showsEverything() {
- showPersonAtIndex(model, INDEX_FIRST_PERSON);
- assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, 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 5bc11d3cdaa..00000000000
--- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.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.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.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.Messages;
-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 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_repeatedNonTagValue_failure() {
- String validExpectedPersonString = NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_FRIEND;
-
- // multiple names
- assertParseFailure(parser, NAME_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
-
- // multiple phones
- assertParseFailure(parser, PHONE_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // multiple emails
- assertParseFailure(parser, EMAIL_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
-
- // multiple addresses
- assertParseFailure(parser, ADDRESS_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
-
- // multiple fields repeated
- assertParseFailure(parser,
- validExpectedPersonString + PHONE_DESC_AMY + EMAIL_DESC_AMY + NAME_DESC_AMY + ADDRESS_DESC_AMY
- + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ADDRESS, PREFIX_EMAIL, PREFIX_PHONE));
-
- // invalid value followed by valid value
-
- // invalid name
- assertParseFailure(parser, INVALID_NAME_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
-
- // invalid email
- assertParseFailure(parser, INVALID_EMAIL_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
-
- // invalid phone
- assertParseFailure(parser, INVALID_PHONE_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // invalid address
- assertParseFailure(parser, INVALID_ADDRESS_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
-
- // valid value followed by invalid value
-
- // invalid name
- assertParseFailure(parser, validExpectedPersonString + INVALID_NAME_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
-
- // invalid email
- assertParseFailure(parser, validExpectedPersonString + INVALID_EMAIL_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
-
- // invalid phone
- assertParseFailure(parser, validExpectedPersonString + INVALID_PHONE_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // invalid address
- assertParseFailure(parser, validExpectedPersonString + INVALID_ADDRESS_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
- }
-
- @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/seedu/address/logic/parser/AddressBookParserTest.java
deleted file mode 100644
index 5a1ab3dbc0c..00000000000
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package seedu.address.logic.parser;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
-import static seedu.address.testutil.Assert.assertThrows;
-import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-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;
-
-public class AddressBookParserTest {
-
- private final AddressBookParser parser = new AddressBookParser();
-
- @Test
- public void parseCommand_add() throws Exception {
- Person person = new PersonBuilder().build();
- AddCommand command = (AddCommand) parser.parseCommand(PersonUtil.getAddCommand(person));
- assertEquals(new AddCommand(person), command);
- }
-
- @Test
- public void parseCommand_clear() throws Exception {
- assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand);
- assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand);
- }
-
- @Test
- public void parseCommand_delete() throws Exception {
- DeleteCommand command = (DeleteCommand) parser.parseCommand(
- DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
- assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command);
- }
-
- @Test
- 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));
- assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command);
- }
-
- @Test
- public void parseCommand_exit() throws Exception {
- assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand);
- assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand);
- }
-
- @Test
- 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(" ")));
- assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command);
- }
-
- @Test
- public void parseCommand_help() throws Exception {
- assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand);
- assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand);
- }
-
- @Test
- public void parseCommand_list() throws Exception {
- assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand);
- assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand);
- }
-
- @Test
- public void parseCommand_unrecognisedInput_throwsParseException() {
- 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"));
- }
-}
diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
deleted file mode 100644
index cc7175172d4..00000000000
--- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.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_EMAIL_AMY;
-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_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-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;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.Messages;
-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;
-
-public class EditCommandParserTest {
-
- private static final String TAG_EMPTY = " " + PREFIX_TAG;
-
- private static final String MESSAGE_INVALID_FORMAT =
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE);
-
- private EditCommandParser parser = new EditCommandParser();
-
- @Test
- public void parse_missingParts_failure() {
- // no index specified
- assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT);
-
- // no field specified
- assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED);
-
- // no index and no field specified
- assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT);
- }
-
- @Test
- public void parse_invalidPreamble_failure() {
- // negative index
- assertParseFailure(parser, "-5" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT);
-
- // zero index
- assertParseFailure(parser, "0" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT);
-
- // invalid arguments being parsed as preamble
- assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT);
-
- // invalid prefix being parsed as preamble
- assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT);
- }
-
- @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_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);
-
- // 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);
-
- // 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,
- Name.MESSAGE_CONSTRAINTS);
- }
-
- @Test
- public void parse_allFieldsSpecified_success() {
- Index targetIndex = INDEX_SECOND_PERSON;
- String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + TAG_DESC_HUSBAND
- + 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)
- .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
-
- assertParseSuccess(parser, userInput, expectedCommand);
- }
-
- @Test
- 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();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
-
- assertParseSuccess(parser, userInput, expectedCommand);
- }
-
- @Test
- 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();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
-
- // phone
- userInput = targetIndex.getOneBased() + PHONE_DESC_AMY;
- descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build();
- expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
-
- // email
- userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY;
- descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build();
- expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
-
- // address
- userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY;
- descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build();
- expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
-
- // tags
- userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND;
- descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build();
- expectedCommand = new EditCommand(targetIndex, descriptor);
- assertParseSuccess(parser, userInput, expectedCommand);
- }
-
- @Test
- public void parse_multipleRepeatedFields_failure() {
- // More extensive testing of duplicate parameter detections is done in
- // AddCommandParserTest#parse_repeatedNonTagValue_failure()
-
- // valid followed by invalid
- Index targetIndex = INDEX_FIRST_PERSON;
- String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB;
-
- assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // invalid followed by valid
- userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + INVALID_PHONE_DESC;
-
- assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // mulltiple valid fields repeated
- 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;
-
- assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
-
- // multiple invalid values
- userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC
- + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC;
-
- assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
- }
-
- @Test
- public void parse_resetTags_success() {
- Index targetIndex = INDEX_THIRD_PERSON;
- String userInput = targetIndex.getOneBased() + TAG_EMPTY;
-
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build();
- EditCommand 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/seedu/address/logic/parser/FindCommandParserTest.java
deleted file mode 100644
index d92e64d12f9..00000000000
--- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
-import static seedu.address.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;
-
-public class FindCommandParserTest {
-
- private FindCommandParser parser = new FindCommandParser();
-
- @Test
- public void parse_emptyArg_throwsParseException() {
- assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
- }
-
- @Test
- public void parse_validArgs_returnsFindCommand() {
- // no leading and trailing whitespaces
- FindCommand expectedFindCommand =
- new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")));
- assertParseSuccess(parser, "Alice Bob", expectedFindCommand);
-
- // multiple whitespaces between keywords
- assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand);
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
deleted file mode 100644
index 4256788b1a7..00000000000
--- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-package seedu.address.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 java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-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;
-
-public class ParserUtilTest {
- private static final String INVALID_NAME = "R@chel";
- private static final String INVALID_PHONE = "+651234";
- private static final String INVALID_ADDRESS = " ";
- private static final String INVALID_EMAIL = "example.com";
- private static final String INVALID_TAG = "#friend";
-
- private static final String VALID_NAME = "Rachel Walker";
- private static final String VALID_PHONE = "123456";
- private static final String VALID_ADDRESS = "123 Main Street #0505";
- private static final String VALID_EMAIL = "rachel@example.com";
- private static final String VALID_TAG_1 = "friend";
- private static final String VALID_TAG_2 = "neighbour";
-
- private static final String WHITESPACE = " \t\r\n";
-
- @Test
- public void parseIndex_invalidInput_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a"));
- }
-
- @Test
- public void parseIndex_outOfRangeInput_throwsParseException() {
- assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, ()
- -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1)));
- }
-
- @Test
- public void parseIndex_validInput_success() throws Exception {
- // No whitespaces
- assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex("1"));
-
- // Leading and trailing whitespaces
- assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 "));
- }
-
- @Test
- public void parseName_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseName((String) null));
- }
-
- @Test
- public void parseName_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseName(INVALID_NAME));
- }
-
- @Test
- public void parseName_validValueWithoutWhitespace_returnsName() throws Exception {
- Name expectedName = new Name(VALID_NAME);
- assertEquals(expectedName, ParserUtil.parseName(VALID_NAME));
- }
-
- @Test
- public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Exception {
- String nameWithWhitespace = WHITESPACE + VALID_NAME + WHITESPACE;
- Name expectedName = new Name(VALID_NAME);
- assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace));
- }
-
- @Test
- public void parsePhone_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parsePhone((String) null));
- }
-
- @Test
- public void parsePhone_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parsePhone(INVALID_PHONE));
- }
-
- @Test
- public void parsePhone_validValueWithoutWhitespace_returnsPhone() throws Exception {
- Phone expectedPhone = new Phone(VALID_PHONE);
- assertEquals(expectedPhone, ParserUtil.parsePhone(VALID_PHONE));
- }
-
- @Test
- public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exception {
- String phoneWithWhitespace = WHITESPACE + VALID_PHONE + WHITESPACE;
- Phone expectedPhone = new Phone(VALID_PHONE);
- assertEquals(expectedPhone, ParserUtil.parsePhone(phoneWithWhitespace));
- }
-
- @Test
- public void parseAddress_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseAddress((String) null));
- }
-
- @Test
- public void parseAddress_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseAddress(INVALID_ADDRESS));
- }
-
- @Test
- public void parseAddress_validValueWithoutWhitespace_returnsAddress() throws Exception {
- Address expectedAddress = new Address(VALID_ADDRESS);
- assertEquals(expectedAddress, ParserUtil.parseAddress(VALID_ADDRESS));
- }
-
- @Test
- public void parseAddress_validValueWithWhitespace_returnsTrimmedAddress() throws Exception {
- String addressWithWhitespace = WHITESPACE + VALID_ADDRESS + WHITESPACE;
- Address expectedAddress = new Address(VALID_ADDRESS);
- assertEquals(expectedAddress, ParserUtil.parseAddress(addressWithWhitespace));
- }
-
- @Test
- public void parseEmail_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseEmail((String) null));
- }
-
- @Test
- public void parseEmail_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL));
- }
-
- @Test
- public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception {
- Email expectedEmail = new Email(VALID_EMAIL);
- assertEquals(expectedEmail, ParserUtil.parseEmail(VALID_EMAIL));
- }
-
- @Test
- public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception {
- String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE;
- Email expectedEmail = new Email(VALID_EMAIL);
- assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace));
- }
-
- @Test
- public void parseTag_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null));
- }
-
- @Test
- public void parseTag_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG));
- }
-
- @Test
- public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception {
- Tag expectedTag = new Tag(VALID_TAG_1);
- assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1));
- }
-
- @Test
- public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception {
- String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE;
- Tag expectedTag = new Tag(VALID_TAG_1);
- assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace));
- }
-
- @Test
- public void parseTags_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null));
- }
-
- @Test
- public void parseTags_collectionWithInvalidTags_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG)));
- }
-
- @Test
- public void parseTags_emptyCollection_returnsEmptySet() throws Exception {
- assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty());
- }
-
- @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)));
-
- assertEquals(expectedTagSet, actualTagSet);
- }
-}
diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java
deleted file mode 100644
index 68c8c5ba4d5..00000000000
--- a/src/test/java/seedu/address/model/AddressBookTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package seedu.address.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 java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-
-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;
-
-public class AddressBookTest {
-
- private final AddressBook addressBook = new AddressBook();
-
- @Test
- public void constructor() {
- assertEquals(Collections.emptyList(), addressBook.getPersonList());
- }
-
- @Test
- public void resetData_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> addressBook.resetData(null));
- }
-
- @Test
- public void resetData_withValidReadOnlyAddressBook_replacesData() {
- AddressBook newData = getTypicalAddressBook();
- addressBook.resetData(newData);
- assertEquals(newData, addressBook);
- }
-
- @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();
- List newPersons = Arrays.asList(ALICE, editedAlice);
- AddressBookStub newData = new AddressBookStub(newPersons);
-
- assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData));
- }
-
- @Test
- public void hasPerson_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> addressBook.hasPerson(null));
- }
-
- @Test
- public void hasPerson_personNotInAddressBook_returnsFalse() {
- assertFalse(addressBook.hasPerson(ALICE));
- }
-
- @Test
- public void hasPerson_personInAddressBook_returnsTrue() {
- addressBook.addPerson(ALICE);
- assertTrue(addressBook.hasPerson(ALICE));
- }
-
- @Test
- public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() {
- addressBook.addPerson(ALICE);
- 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));
- }
-
- @Test
- public void toStringMethod() {
- String expected = AddressBook.class.getCanonicalName() + "{persons=" + addressBook.getPersonList() + "}";
- assertEquals(expected, addressBook.toString());
- }
-
- /**
- * A stub ReadOnlyAddressBook whose persons list can violate interface constraints.
- */
- private static class AddressBookStub implements ReadOnlyAddressBook {
- private final ObservableList persons = FXCollections.observableArrayList();
-
- AddressBookStub(Collection persons) {
- this.persons.setAll(persons);
- }
-
- @Override
- public ObservableList getPersonList() {
- return persons;
- }
- }
-
-}
diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java
deleted file mode 100644
index 2cf1418d116..00000000000
--- a/src/test/java/seedu/address/model/ModelManagerTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package seedu.address.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 java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.commons.core.GuiSettings;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-import seedu.address.testutil.AddressBookBuilder;
-
-public class ModelManagerTest {
-
- private ModelManager modelManager = new ModelManager();
-
- @Test
- public void constructor() {
- assertEquals(new UserPrefs(), modelManager.getUserPrefs());
- assertEquals(new GuiSettings(), modelManager.getGuiSettings());
- assertEquals(new AddressBook(), new AddressBook(modelManager.getAddressBook()));
- }
-
- @Test
- public void setUserPrefs_nullUserPrefs_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> modelManager.setUserPrefs(null));
- }
-
- @Test
- public void setUserPrefs_validUserPrefs_copiesUserPrefs() {
- UserPrefs userPrefs = new UserPrefs();
- userPrefs.setAddressBookFilePath(Paths.get("address/book/file/path"));
- userPrefs.setGuiSettings(new GuiSettings(1, 2, 3, 4));
- modelManager.setUserPrefs(userPrefs);
- assertEquals(userPrefs, modelManager.getUserPrefs());
-
- // Modifying userPrefs should not modify modelManager's userPrefs
- UserPrefs oldUserPrefs = new UserPrefs(userPrefs);
- userPrefs.setAddressBookFilePath(Paths.get("new/address/book/file/path"));
- assertEquals(oldUserPrefs, modelManager.getUserPrefs());
- }
-
- @Test
- public void setGuiSettings_nullGuiSettings_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> modelManager.setGuiSettings(null));
- }
-
- @Test
- public void setGuiSettings_validGuiSettings_setsGuiSettings() {
- GuiSettings guiSettings = new GuiSettings(1, 2, 3, 4);
- modelManager.setGuiSettings(guiSettings);
- assertEquals(guiSettings, modelManager.getGuiSettings());
- }
-
- @Test
- public void setAddressBookFilePath_nullPath_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> modelManager.setAddressBookFilePath(null));
- }
-
- @Test
- public void setAddressBookFilePath_validPath_setsAddressBookFilePath() {
- Path path = Paths.get("address/book/file/path");
- modelManager.setAddressBookFilePath(path);
- assertEquals(path, modelManager.getAddressBookFilePath());
- }
-
- @Test
- public void hasPerson_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null));
- }
-
- @Test
- public void hasPerson_personNotInAddressBook_returnsFalse() {
- assertFalse(modelManager.hasPerson(ALICE));
- }
-
- @Test
- public void hasPerson_personInAddressBook_returnsTrue() {
- modelManager.addPerson(ALICE);
- assertTrue(modelManager.hasPerson(ALICE));
- }
-
- @Test
- public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() {
- assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0));
- }
-
- @Test
- public void equals() {
- AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build();
- AddressBook differentAddressBook = new AddressBook();
- UserPrefs userPrefs = new UserPrefs();
-
- // same values -> returns true
- modelManager = new ModelManager(addressBook, userPrefs);
- ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs);
- assertTrue(modelManager.equals(modelManagerCopy));
-
- // same object -> returns true
- assertTrue(modelManager.equals(modelManager));
-
- // null -> returns false
- assertFalse(modelManager.equals(null));
-
- // different types -> returns false
- assertFalse(modelManager.equals(5));
-
- // different addressBook -> returns false
- assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs)));
-
- // different filteredList -> returns false
- String[] keywords = ALICE.getName().fullName.split("\\s+");
- modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords)));
- assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs)));
-
- // resets modelManager to initial state for upcoming tests
- modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
-
- // different userPrefs -> returns false
- UserPrefs differentUserPrefs = new UserPrefs();
- differentUserPrefs.setAddressBookFilePath(Paths.get("differentFilePath"));
- assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs)));
- }
-}
diff --git a/src/test/java/seedu/address/model/UserPrefsTest.java b/src/test/java/seedu/address/model/UserPrefsTest.java
deleted file mode 100644
index b1307a70d52..00000000000
--- a/src/test/java/seedu/address/model/UserPrefsTest.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package seedu.address.model;
-
-import static seedu.address.testutil.Assert.assertThrows;
-
-import org.junit.jupiter.api.Test;
-
-public class UserPrefsTest {
-
- @Test
- public void setGuiSettings_nullGuiSettings_throwsNullPointerException() {
- UserPrefs userPref = new UserPrefs();
- assertThrows(NullPointerException.class, () -> userPref.setGuiSettings(null));
- }
-
- @Test
- public void setAddressBookFilePath_nullPath_throwsNullPointerException() {
- UserPrefs userPrefs = new UserPrefs();
- assertThrows(NullPointerException.class, () -> userPrefs.setAddressBookFilePath(null));
- }
-
-}
diff --git a/src/test/java/seedu/address/model/person/AddressTest.java b/src/test/java/seedu/address/model/person/AddressTest.java
deleted file mode 100644
index 314885eca26..00000000000
--- a/src/test/java/seedu/address/model/person/AddressTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package seedu.address.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 org.junit.jupiter.api.Test;
-
-public class AddressTest {
-
- @Test
- public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Address(null));
- }
-
- @Test
- public void constructor_invalidAddress_throwsIllegalArgumentException() {
- String invalidAddress = "";
- assertThrows(IllegalArgumentException.class, () -> new Address(invalidAddress));
- }
-
- @Test
- public void isValidAddress() {
- // null address
- assertThrows(NullPointerException.class, () -> Address.isValidAddress(null));
-
- // invalid addresses
- assertFalse(Address.isValidAddress("")); // empty string
- assertFalse(Address.isValidAddress(" ")); // spaces only
-
- // 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
- }
-
- @Test
- public void equals() {
- Address address = new Address("Valid Address");
-
- // same values -> returns true
- assertTrue(address.equals(new Address("Valid Address")));
-
- // same object -> returns true
- assertTrue(address.equals(address));
-
- // null -> returns false
- assertFalse(address.equals(null));
-
- // different types -> returns false
- assertFalse(address.equals(5.0f));
-
- // different values -> returns false
- assertFalse(address.equals(new Address("Other Valid Address")));
- }
-}
diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/seedu/address/model/person/EmailTest.java
deleted file mode 100644
index f08cdff0a64..00000000000
--- a/src/test/java/seedu/address/model/person/EmailTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package seedu.address.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 org.junit.jupiter.api.Test;
-
-public class EmailTest {
-
- @Test
- public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Email(null));
- }
-
- @Test
- public void constructor_invalidEmail_throwsIllegalArgumentException() {
- String invalidEmail = "";
- assertThrows(IllegalArgumentException.class, () -> new Email(invalidEmail));
- }
-
- @Test
- public void isValidEmail() {
- // null email
- assertThrows(NullPointerException.class, () -> Email.isValidEmail(null));
-
- // blank email
- assertFalse(Email.isValidEmail("")); // empty string
- assertFalse(Email.isValidEmail(" ")); // spaces only
-
- // missing parts
- assertFalse(Email.isValidEmail("@example.com")); // missing local part
- assertFalse(Email.isValidEmail("peterjackexample.com")); // missing '@' symbol
- assertFalse(Email.isValidEmail("peterjack@")); // missing domain name
-
- // invalid parts
- assertFalse(Email.isValidEmail("peterjack@-")); // invalid domain name
- assertFalse(Email.isValidEmail("peterjack@exam_ple.com")); // underscore in domain name
- assertFalse(Email.isValidEmail("peter jack@example.com")); // spaces in local part
- assertFalse(Email.isValidEmail("peterjack@exam ple.com")); // spaces in domain name
- assertFalse(Email.isValidEmail(" peterjack@example.com")); // leading space
- 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 ends with a hyphen
- 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 ends with a period
- 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
-
- // valid email
- assertTrue(Email.isValidEmail("PeterJack_1190@example.com")); // underscore in local part
- assertTrue(Email.isValidEmail("PeterJack.1190@example.com")); // period in local part
- assertTrue(Email.isValidEmail("PeterJack+1190@example.com")); // '+' symbol in local part
- assertTrue(Email.isValidEmail("PeterJack-1190@example.com")); // hyphen in local part
- 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("e1234567@u.nus.edu")); // more than one period in domain
- }
-
- @Test
- public void equals() {
- Email email = new Email("valid@email");
-
- // same values -> returns true
- assertTrue(email.equals(new Email("valid@email")));
-
- // same object -> returns true
- assertTrue(email.equals(email));
-
- // null -> returns false
- assertFalse(email.equals(null));
-
- // different types -> returns false
- assertFalse(email.equals(5.0f));
-
- // different values -> returns false
- assertFalse(email.equals(new Email("other.valid@email")));
- }
-}
diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
deleted file mode 100644
index 6b3fd90ade7..00000000000
--- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package seedu.address.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 java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.testutil.PersonBuilder;
-
-public class NameContainsKeywordsPredicateTest {
-
- @Test
- public void equals() {
- List firstPredicateKeywordList = Collections.singletonList("first");
- List secondPredicateKeywordList = Arrays.asList("first", "second");
-
- 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);
- assertTrue(firstPredicate.equals(firstPredicateCopy));
-
- // different types -> returns false
- assertFalse(firstPredicate.equals(1));
-
- // null -> returns false
- assertFalse(firstPredicate.equals(null));
-
- // different person -> returns false
- assertFalse(firstPredicate.equals(secondPredicate));
- }
-
- @Test
- public void test_nameContainsKeywords_returnsTrue() {
- // One keyword
- NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.singletonList("Alice"));
- assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
-
- // Multiple keywords
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob"));
- assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
-
- // Only one matching keyword
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol"));
- assertTrue(predicate.test(new PersonBuilder().withName("Alice Carol").build()));
-
- // Mixed-case keywords
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB"));
- assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
- }
-
- @Test
- public void test_nameDoesNotContainKeywords_returnsFalse() {
- // Zero keywords
- NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.emptyList());
- assertFalse(predicate.test(new PersonBuilder().withName("Alice").build()));
-
- // Non-matching keyword
- predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol"));
- 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"));
- assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345")
- .withEmail("alice@email.com").withAddress("Main Street").build()));
- }
-
- @Test
- public void toStringMethod() {
- List keywords = List.of("keyword1", "keyword2");
- NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(keywords);
-
- String expected = NameContainsKeywordsPredicate.class.getCanonicalName() + "{keywords=" + keywords + "}";
- assertEquals(expected, predicate.toString());
- }
-}
diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/seedu/address/model/person/NameTest.java
deleted file mode 100644
index 94e3dd726bd..00000000000
--- a/src/test/java/seedu/address/model/person/NameTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.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 org.junit.jupiter.api.Test;
-
-public class NameTest {
-
- @Test
- public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Name(null));
- }
-
- @Test
- public void constructor_invalidName_throwsIllegalArgumentException() {
- String invalidName = "";
- assertThrows(IllegalArgumentException.class, () -> new Name(invalidName));
- }
-
- @Test
- public void isValidName() {
- // null name
- assertThrows(NullPointerException.class, () -> Name.isValidName(null));
-
- // invalid name
- assertFalse(Name.isValidName("")); // empty string
- assertFalse(Name.isValidName(" ")); // spaces only
- assertFalse(Name.isValidName("^")); // only non-alphanumeric characters
- assertFalse(Name.isValidName("peter*")); // contains non-alphanumeric characters
-
- // valid name
- assertTrue(Name.isValidName("peter jack")); // alphabets only
- assertTrue(Name.isValidName("12345")); // numbers only
- assertTrue(Name.isValidName("peter the 2nd")); // alphanumeric characters
- assertTrue(Name.isValidName("Capital Tan")); // with capital letters
- assertTrue(Name.isValidName("David Roger Jackson Ray Jr 2nd")); // long names
- }
-
- @Test
- public void equals() {
- Name name = new Name("Valid Name");
-
- // same values -> returns true
- assertTrue(name.equals(new Name("Valid Name")));
-
- // same object -> returns true
- assertTrue(name.equals(name));
-
- // null -> returns false
- assertFalse(name.equals(null));
-
- // different types -> returns false
- assertFalse(name.equals(5.0f));
-
- // different values -> returns false
- assertFalse(name.equals(new Name("Other Valid Name")));
- }
-}
diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java
deleted file mode 100644
index 31a10d156c9..00000000000
--- a/src/test/java/seedu/address/model/person/PersonTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package seedu.address.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_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 org.junit.jupiter.api.Test;
-
-import seedu.address.testutil.PersonBuilder;
-
-public class PersonTest {
-
- @Test
- public void asObservableList_modifyList_throwsUnsupportedOperationException() {
- Person person = new PersonBuilder().build();
- assertThrows(UnsupportedOperationException.class, () -> person.getTags().remove(0));
- }
-
- @Test
- public void isSamePerson() {
- // same object -> returns true
- assertTrue(ALICE.isSamePerson(ALICE));
-
- // null -> returns false
- 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();
- assertTrue(ALICE.isSamePerson(editedAlice));
-
- // different name, all other attributes same -> returns false
- editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
- assertFalse(ALICE.isSamePerson(editedAlice));
-
- // name differs in case, all other attributes same -> returns false
- Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build();
- assertFalse(BOB.isSamePerson(editedBob));
-
- // name has trailing spaces, all other attributes same -> returns false
- String nameWithTrailingSpaces = VALID_NAME_BOB + " ";
- editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build();
- assertFalse(BOB.isSamePerson(editedBob));
- }
-
- @Test
- public void equals() {
- // same values -> returns true
- Person aliceCopy = new PersonBuilder(ALICE).build();
- assertTrue(ALICE.equals(aliceCopy));
-
- // same object -> returns true
- assertTrue(ALICE.equals(ALICE));
-
- // null -> returns false
- assertFalse(ALICE.equals(null));
-
- // different type -> returns false
- assertFalse(ALICE.equals(5));
-
- // different person -> returns false
- assertFalse(ALICE.equals(BOB));
-
- // different name -> returns false
- Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
-
- // different phone -> returns false
- editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
-
- // different email -> returns false
- editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
-
- // different address -> returns false
- editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
-
- // different tags -> returns false
- editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build();
- assertFalse(ALICE.equals(editedAlice));
- }
-
- @Test
- public void toStringMethod() {
- String expected = Person.class.getCanonicalName() + "{name=" + ALICE.getName() + ", phone=" + ALICE.getPhone()
- + ", email=" + ALICE.getEmail() + ", address=" + ALICE.getAddress() + ", tags=" + ALICE.getTags() + "}";
- assertEquals(expected, ALICE.toString());
- }
-}
diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/seedu/address/model/person/PhoneTest.java
deleted file mode 100644
index deaaa5ba190..00000000000
--- a/src/test/java/seedu/address/model/person/PhoneTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package seedu.address.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 org.junit.jupiter.api.Test;
-
-public class PhoneTest {
-
- @Test
- public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Phone(null));
- }
-
- @Test
- public void constructor_invalidPhone_throwsIllegalArgumentException() {
- String invalidPhone = "";
- assertThrows(IllegalArgumentException.class, () -> new Phone(invalidPhone));
- }
-
- @Test
- public void isValidPhone() {
- // null phone number
- assertThrows(NullPointerException.class, () -> Phone.isValidPhone(null));
-
- // invalid phone numbers
- assertFalse(Phone.isValidPhone("")); // empty string
- assertFalse(Phone.isValidPhone(" ")); // spaces only
- assertFalse(Phone.isValidPhone("91")); // less than 3 numbers
- assertFalse(Phone.isValidPhone("phone")); // non-numeric
- assertFalse(Phone.isValidPhone("9011p041")); // alphabets within digits
- assertFalse(Phone.isValidPhone("9312 1534")); // spaces within digits
-
- // valid phone numbers
- assertTrue(Phone.isValidPhone("911")); // exactly 3 numbers
- assertTrue(Phone.isValidPhone("93121534"));
- assertTrue(Phone.isValidPhone("124293842033123")); // long phone numbers
- }
-
- @Test
- public void equals() {
- Phone phone = new Phone("999");
-
- // same values -> returns true
- assertTrue(phone.equals(new Phone("999")));
-
- // same object -> returns true
- assertTrue(phone.equals(phone));
-
- // null -> returns false
- assertFalse(phone.equals(null));
-
- // different types -> returns false
- assertFalse(phone.equals(5.0f));
-
- // different values -> returns false
- assertFalse(phone.equals(new Phone("995")));
- }
-}
diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
deleted file mode 100644
index 17ae501df08..00000000000
--- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java
+++ /dev/null
@@ -1,175 +0,0 @@
-package seedu.address.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 java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-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;
-
-public class UniquePersonListTest {
-
- private final UniquePersonList uniquePersonList = new UniquePersonList();
-
- @Test
- public void contains_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.contains(null));
- }
-
- @Test
- public void contains_personNotInList_returnsFalse() {
- assertFalse(uniquePersonList.contains(ALICE));
- }
-
- @Test
- public void contains_personInList_returnsTrue() {
- uniquePersonList.add(ALICE);
- assertTrue(uniquePersonList.contains(ALICE));
- }
-
- @Test
- public void contains_personWithSameIdentityFieldsInList_returnsTrue() {
- uniquePersonList.add(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
- .build();
- assertTrue(uniquePersonList.contains(editedAlice));
- }
-
- @Test
- public void add_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.add(null));
- }
-
- @Test
- public void add_duplicatePerson_throwsDuplicatePersonException() {
- uniquePersonList.add(ALICE);
- assertThrows(DuplicatePersonException.class, () -> uniquePersonList.add(ALICE));
- }
-
- @Test
- public void setPerson_nullTargetPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(null, ALICE));
- }
-
- @Test
- public void setPerson_nullEditedPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(ALICE, null));
- }
-
- @Test
- public void setPerson_targetPersonNotInList_throwsPersonNotFoundException() {
- assertThrows(PersonNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE));
- }
-
- @Test
- public void setPerson_editedPersonIsSamePerson_success() {
- uniquePersonList.add(ALICE);
- uniquePersonList.setPerson(ALICE, ALICE);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- expectedUniquePersonList.add(ALICE);
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPerson_editedPersonHasSameIdentity_success() {
- uniquePersonList.add(ALICE);
- 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);
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPerson_editedPersonHasDifferentIdentity_success() {
- uniquePersonList.add(ALICE);
- uniquePersonList.setPerson(ALICE, BOB);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- expectedUniquePersonList.add(BOB);
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPerson_editedPersonHasNonUniqueIdentity_throwsDuplicatePersonException() {
- uniquePersonList.add(ALICE);
- uniquePersonList.add(BOB);
- assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPerson(ALICE, BOB));
- }
-
- @Test
- public void remove_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.remove(null));
- }
-
- @Test
- public void remove_personDoesNotExist_throwsPersonNotFoundException() {
- assertThrows(PersonNotFoundException.class, () -> uniquePersonList.remove(ALICE));
- }
-
- @Test
- public void remove_existingPerson_removesPerson() {
- uniquePersonList.add(ALICE);
- uniquePersonList.remove(ALICE);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPersons_nullUniquePersonList_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((UniquePersonList) null));
- }
-
- @Test
- public void setPersons_uniquePersonList_replacesOwnListWithProvidedUniquePersonList() {
- uniquePersonList.add(ALICE);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- expectedUniquePersonList.add(BOB);
- uniquePersonList.setPersons(expectedUniquePersonList);
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPersons_nullList_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((List) null));
- }
-
- @Test
- public void setPersons_list_replacesOwnListWithProvidedList() {
- uniquePersonList.add(ALICE);
- List personList = Collections.singletonList(BOB);
- uniquePersonList.setPersons(personList);
- UniquePersonList expectedUniquePersonList = new UniquePersonList();
- expectedUniquePersonList.add(BOB);
- assertEquals(expectedUniquePersonList, uniquePersonList);
- }
-
- @Test
- public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() {
- List listWithDuplicatePersons = Arrays.asList(ALICE, ALICE);
- assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPersons(listWithDuplicatePersons));
- }
-
- @Test
- public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() {
- assertThrows(UnsupportedOperationException.class, ()
- -> uniquePersonList.asUnmodifiableObservableList().remove(0));
- }
-
- @Test
- public void toStringMethod() {
- assertEquals(uniquePersonList.asUnmodifiableObservableList().toString(), uniquePersonList.toString());
- }
-}
diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/seedu/address/model/tag/TagTest.java
deleted file mode 100644
index 64d07d79ee2..00000000000
--- a/src/test/java/seedu/address/model/tag/TagTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package seedu.address.model.tag;
-
-import static seedu.address.testutil.Assert.assertThrows;
-
-import org.junit.jupiter.api.Test;
-
-public class TagTest {
-
- @Test
- public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Tag(null));
- }
-
- @Test
- public void constructor_invalidTagName_throwsIllegalArgumentException() {
- String invalidTagName = "";
- assertThrows(IllegalArgumentException.class, () -> new Tag(invalidTagName));
- }
-
- @Test
- public void isValidTagName() {
- // null tag name
- assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null));
- }
-
-}
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
deleted file mode 100644
index 83b11331cdb..00000000000
--- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package seedu.address.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 java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-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;
-
-public class JsonAdaptedPersonTest {
- private static final String INVALID_NAME = "R@chel";
- private static final String INVALID_PHONE = "+651234";
- private static final String INVALID_ADDRESS = " ";
- private static final String INVALID_EMAIL = "example.com";
- private static final String INVALID_TAG = "#friend";
-
- private static final String VALID_NAME = BENSON.getName().toString();
- private static final String VALID_PHONE = BENSON.getPhone().toString();
- private static final String VALID_EMAIL = BENSON.getEmail().toString();
- private static final String VALID_ADDRESS = BENSON.getAddress().toString();
- private static final List VALID_TAGS = BENSON.getTags().stream()
- .map(JsonAdaptedTag::new)
- .collect(Collectors.toList());
-
- @Test
- public void toModelType_validPersonDetails_returnsPerson() throws Exception {
- JsonAdaptedPerson person = new JsonAdaptedPerson(BENSON);
- assertEquals(BENSON, person.toModelType());
- }
-
- @Test
- public void toModelType_invalidName_throwsIllegalValueException() {
- JsonAdaptedPerson person =
- 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());
- 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);
- 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());
- 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);
- 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());
- 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);
- 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());
- assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
- }
-
- @Test
- 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);
- assertThrows(IllegalValueException.class, person::toModelType);
- }
-
-}
diff --git a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java b/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java
deleted file mode 100644
index 4e5ce9200c8..00000000000
--- a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package seedu.address.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 java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-import seedu.address.commons.exceptions.DataLoadingException;
-import seedu.address.model.AddressBook;
-import seedu.address.model.ReadOnlyAddressBook;
-
-public class JsonAddressBookStorageTest {
- private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonAddressBookStorageTest");
-
- @TempDir
- public Path testFolder;
-
- @Test
- 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 Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) {
- return prefsFileInTestDataFolder != null
- ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder)
- : null;
- }
-
- @Test
- public void read_missingFile_emptyResult() throws Exception {
- assertFalse(readAddressBook("NonExistentFile.json").isPresent());
- }
-
- @Test
- public void read_notJsonFormat_exceptionThrown() {
- assertThrows(DataLoadingException.class, () -> readAddressBook("notJsonFormatAddressBook.json"));
- }
-
- @Test
- public void readAddressBook_invalidPersonAddressBook_throwDataLoadingException() {
- assertThrows(DataLoadingException.class, () -> readAddressBook("invalidPersonAddressBook.json"));
- }
-
- @Test
- public void readAddressBook_invalidAndValidPersonAddressBook_throwDataLoadingException() {
- assertThrows(DataLoadingException.class, () -> readAddressBook("invalidAndValidPersonAddressBook.json"));
- }
-
- @Test
- public void readAndSaveAddressBook_allInOrder_success() throws Exception {
- Path filePath = testFolder.resolve("TempAddressBook.json");
- AddressBook original = getTypicalAddressBook();
- JsonAddressBookStorage jsonAddressBookStorage = new JsonAddressBookStorage(filePath);
-
- // Save in new file and read back
- jsonAddressBookStorage.saveAddressBook(original, filePath);
- ReadOnlyAddressBook readBack = jsonAddressBookStorage.readAddressBook(filePath).get();
- assertEquals(original, new AddressBook(readBack));
-
- // Modify data, overwrite exiting file, and read back
- original.addPerson(HOON);
- original.removePerson(ALICE);
- jsonAddressBookStorage.saveAddressBook(original, filePath);
- readBack = jsonAddressBookStorage.readAddressBook(filePath).get();
- assertEquals(original, new AddressBook(readBack));
-
- // Save and read without specifying file path
- original.addPerson(IDA);
- jsonAddressBookStorage.saveAddressBook(original); // file path not specified
- readBack = jsonAddressBookStorage.readAddressBook().get(); // file path not specified
- assertEquals(original, new AddressBook(readBack));
-
- }
-
- @Test
- public void saveAddressBook_nullAddressBook_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> saveAddressBook(null, "SomeFile.json"));
- }
-
- /**
- * Saves {@code addressBook} at the specified {@code filePath}.
- */
- private void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) {
- try {
- new JsonAddressBookStorage(Paths.get(filePath))
- .saveAddressBook(addressBook, addToTestDataPathIfNotNull(filePath));
- } catch (IOException ioe) {
- throw new AssertionError("There should not be an error writing to the file.", ioe);
- }
- }
-
- @Test
- public void saveAddressBook_nullFilePath_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> saveAddressBook(new AddressBook(), null));
- }
-}
diff --git a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java
deleted file mode 100644
index 188c9058d20..00000000000
--- a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package seedu.address.storage;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static seedu.address.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;
-
-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");
-
- @Test
- public void toModelType_typicalPersonsFile_success() throws Exception {
- JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(TYPICAL_PERSONS_FILE,
- JsonSerializableAddressBook.class).get();
- AddressBook addressBookFromFile = dataFromFile.toModelType();
- AddressBook typicalPersonsAddressBook = TypicalPersons.getTypicalAddressBook();
- assertEquals(addressBookFromFile, typicalPersonsAddressBook);
- }
-
- @Test
- public void toModelType_invalidPersonFile_throwsIllegalValueException() throws Exception {
- JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(INVALID_PERSON_FILE,
- JsonSerializableAddressBook.class).get();
- assertThrows(IllegalValueException.class, dataFromFile::toModelType);
- }
-
- @Test
- public void toModelType_duplicatePersons_throwsIllegalValueException() throws Exception {
- JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_PERSON_FILE,
- JsonSerializableAddressBook.class).get();
- assertThrows(IllegalValueException.class, JsonSerializableAddressBook.MESSAGE_DUPLICATE_PERSON,
- dataFromFile::toModelType);
- }
-
-}
diff --git a/src/test/java/seedu/address/testutil/AddressBookBuilder.java b/src/test/java/seedu/address/testutil/AddressBookBuilder.java
deleted file mode 100644
index d53799fd110..00000000000
--- a/src/test/java/seedu/address/testutil/AddressBookBuilder.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package seedu.address.testutil;
-
-import seedu.address.model.AddressBook;
-import seedu.address.model.person.Person;
-
-/**
- * A utility class to help with building Addressbook objects.
- * Example usage:
- * {@code AddressBook ab = new AddressBookBuilder().withPerson("John", "Doe").build();}
- */
-public class AddressBookBuilder {
-
- private AddressBook addressBook;
-
- public AddressBookBuilder() {
- addressBook = new AddressBook();
- }
-
- public AddressBookBuilder(AddressBook addressBook) {
- this.addressBook = addressBook;
- }
-
- /**
- * Adds a new {@code Person} to the {@code AddressBook} that we are building.
- */
- public AddressBookBuilder withPerson(Person person) {
- addressBook.addPerson(person);
- return this;
- }
-
- public AddressBook build() {
- return addressBook;
- }
-}
diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
deleted file mode 100644
index 4584bd5044e..00000000000
--- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package seedu.address.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;
-
-/**
- * A utility class to help with building EditPersonDescriptor objects.
- */
-public class EditPersonDescriptorBuilder {
-
- private EditPersonDescriptor descriptor;
-
- public EditPersonDescriptorBuilder() {
- descriptor = new EditPersonDescriptor();
- }
-
- public EditPersonDescriptorBuilder(EditPersonDescriptor descriptor) {
- this.descriptor = new EditPersonDescriptor(descriptor);
- }
-
- /**
- * Returns an {@code EditPersonDescriptor} with fields containing {@code person}'s details
- */
- public EditPersonDescriptorBuilder(Person person) {
- descriptor = new EditPersonDescriptor();
- descriptor.setName(person.getName());
- descriptor.setPhone(person.getPhone());
- descriptor.setEmail(person.getEmail());
- descriptor.setAddress(person.getAddress());
- descriptor.setTags(person.getTags());
- }
-
- /**
- * Sets the {@code Name} of the {@code EditPersonDescriptor} that we are building.
- */
- public EditPersonDescriptorBuilder withName(String name) {
- descriptor.setName(new Name(name));
- return this;
- }
-
- /**
- * Sets the {@code Phone} of the {@code EditPersonDescriptor} that we are building.
- */
- public EditPersonDescriptorBuilder withPhone(String phone) {
- descriptor.setPhone(new Phone(phone));
- return this;
- }
-
- /**
- * Sets the {@code Email} of the {@code EditPersonDescriptor} that we are building.
- */
- public EditPersonDescriptorBuilder withEmail(String email) {
- descriptor.setEmail(new Email(email));
- return this;
- }
-
- /**
- * Sets the {@code Address} of the {@code EditPersonDescriptor} that we are building.
- */
- public EditPersonDescriptorBuilder withAddress(String address) {
- descriptor.setAddress(new Address(address));
- return this;
- }
-
- /**
- * 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());
- descriptor.setTags(tagSet);
- return this;
- }
-
- public EditPersonDescriptor build() {
- return descriptor;
- }
-}
diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java
deleted file mode 100644
index 6be381d39ba..00000000000
--- a/src/test/java/seedu/address/testutil/PersonBuilder.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package seedu.address.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;
-
-/**
- * A utility class to help with building Person objects.
- */
-public class PersonBuilder {
-
- public static final String DEFAULT_NAME = "Amy Bee";
- public static final String DEFAULT_PHONE = "85355255";
- public static final String DEFAULT_EMAIL = "amy@gmail.com";
- public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111";
-
- private Name name;
- private Phone phone;
- private Email email;
- private Address address;
- private Set tags;
-
- /**
- * Creates a {@code PersonBuilder} with the default details.
- */
- public PersonBuilder() {
- name = new Name(DEFAULT_NAME);
- phone = new Phone(DEFAULT_PHONE);
- email = new Email(DEFAULT_EMAIL);
- address = new Address(DEFAULT_ADDRESS);
- tags = new HashSet<>();
- }
-
- /**
- * Initializes the PersonBuilder with the data of {@code personToCopy}.
- */
- public PersonBuilder(Person personToCopy) {
- name = personToCopy.getName();
- phone = personToCopy.getPhone();
- email = personToCopy.getEmail();
- address = personToCopy.getAddress();
- tags = new HashSet<>(personToCopy.getTags());
- }
-
- /**
- * Sets the {@code Name} of the {@code Person} that we are building.
- */
- public PersonBuilder withName(String name) {
- this.name = new Name(name);
- return this;
- }
-
- /**
- * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building.
- */
- public PersonBuilder withTags(String ... tags) {
- this.tags = SampleDataUtil.getTagSet(tags);
- return this;
- }
-
- /**
- * Sets the {@code Address} of the {@code Person} that we are building.
- */
- public PersonBuilder withAddress(String address) {
- this.address = new Address(address);
- return this;
- }
-
- /**
- * Sets the {@code Phone} of the {@code Person} that we are building.
- */
- public PersonBuilder withPhone(String phone) {
- this.phone = new Phone(phone);
- return this;
- }
-
- /**
- * Sets the {@code Email} of the {@code Person} that we are building.
- */
- public PersonBuilder withEmail(String email) {
- this.email = new Email(email);
- return this;
- }
-
- public Person build() {
- return new Person(name, phone, email, address, tags);
- }
-
-}
diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java
deleted file mode 100644
index 90849945183..00000000000
--- a/src/test/java/seedu/address/testutil/PersonUtil.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package seedu.address.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 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;
-
-/**
- * A utility class for Person.
- */
-public class PersonUtil {
-
- /**
- * Returns an add command string for adding the {@code person}.
- */
- public static String getAddCommand(Person person) {
- return AddCommand.COMMAND_WORD + " " + getPersonDetails(person);
- }
-
- /**
- * Returns the part of command string for the given {@code person}'s details.
- */
- public static String getPersonDetails(Person person) {
- StringBuilder sb = new StringBuilder();
- sb.append(PREFIX_NAME + person.getName().fullName + " ");
- sb.append(PREFIX_PHONE + person.getPhone().value + " ");
- 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 + " ")
- );
- return sb.toString();
- }
-
- /**
- * Returns the part of command string for the given {@code EditPersonDescriptor}'s details.
- */
- 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(" "));
- if (descriptor.getTags().isPresent()) {
- Set tags = descriptor.getTags().get();
- if (tags.isEmpty()) {
- sb.append(PREFIX_TAG);
- } else {
- tags.forEach(s -> sb.append(PREFIX_TAG).append(s.tagName).append(" "));
- }
- }
- return sb.toString();
- }
-}
diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/seedu/address/testutil/TypicalIndexes.java
deleted file mode 100644
index 1e613937657..00000000000
--- a/src/test/java/seedu/address/testutil/TypicalIndexes.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package seedu.address.testutil;
-
-import seedu.address.commons.core.index.Index;
-
-/**
- * A utility class containing a list of {@code Index} objects to be used in tests.
- */
-public class TypicalIndexes {
- public static final Index INDEX_FIRST_PERSON = Index.fromOneBased(1);
- public static final Index INDEX_SECOND_PERSON = Index.fromOneBased(2);
- public static final Index INDEX_THIRD_PERSON = Index.fromOneBased(3);
-}
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/ui/TestFxmlObject.java b/src/test/java/seedu/address/ui/TestFxmlObject.java
deleted file mode 100644
index 93f40f1276a..00000000000
--- a/src/test/java/seedu/address/ui/TestFxmlObject.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package seedu.address.ui;
-
-import java.util.Objects;
-
-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.
- */
-@DefaultProperty("text")
-public class TestFxmlObject {
-
- private String text;
-
- public TestFxmlObject() {}
-
- public TestFxmlObject(String text) {
- setText(text);
- }
-
- public String getText() {
- return text;
- }
-
- public void setText(String text) {
- this.text = text;
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof TestFxmlObject)) {
- return false;
- }
-
- TestFxmlObject otherTestFxmlObject = (TestFxmlObject) other;
- return Objects.equals(text, otherTestFxmlObject.text);
- }
-
-}
diff --git a/src/test/java/seedu/address/ui/UiPartTest.java b/src/test/java/seedu/address/ui/UiPartTest.java
deleted file mode 100644
index 33d82d911b8..00000000000
--- a/src/test/java/seedu/address/ui/UiPartTest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package seedu.address.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 java.net.URL;
-import java.nio.file.Path;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-import javafx.fxml.FXML;
-import seedu.address.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 TestFxmlObject VALID_FILE_ROOT = new TestFxmlObject("Hello World!");
-
- @TempDir
- public Path testFolder;
-
- @Test
- public void constructor_nullFileUrl_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new TestUiPart