:information_source: **Info:**
+I am here to provide you with additional information about the commands!
+
+
+:exclamation: **Caution:**
+I am here to warn you of potential risks or issues!
+
+
+If you are an :star::star::star: **experienced user**, skip ahead to [Command summary](#command-summary)
+for a quick refresher.
+
+--------------------------------------------------------------------------------------------------------------------
+
## Quick start
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).
+2. Download the latest `TrAcker.jar` from [here](https://github.com/AY2324S2-CS2103T-T11-4/tp/releases).
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+3. Copy the file to the folder you want to use as the _home folder_ for your **TrAcker** app.
-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.: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.
-Furthermore, certain edits can cause the AddressBook to behave in unexpected ways (e.g., if a value entered is outside of the acceptable range). Therefore, edit the data file only if you are confident that you can update it correctly.
+
:exclamation: **Caution:**
+* If your changes to the data file renders its format invalid, TrAcker will discard all existing data and start with an
+empty data file in the next run. Hence, it is recommended to have a backup of the data file before editing it.
+* Furthermore, certain edits can cause TrAcker to behave in unexpected ways (e.g., if a value entered is outside the
+acceptable range). Therefore, please edit the data file only if you are confident that you can update it correctly.
+
+
+--------------------------------------------------------------------------------------------------------------------
+
+## Tagging
+
+Forget the hassle :hammer: of managing administrative tasks as the Head TA! With TrAcker tags, you can conveniently track
+students' assignment completion status, their assigned tutorial groups as well as their tutorial attendance records.
+You are also able to track TA's assigned tutorial slots and their availability for other tutorial slots in case
+substitutions are needed.
+
+TrAcker allows the use of three different types of tags - **Assignment, Attendance,** and **Tutorial** -
+which can be attached to Students and TAs.
+The different tag types along with their corresponding tag statuses are described below.
+
+### Tag Status
+
+| Tag type | Status |
+|------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Assignment | `cg` :
COMPLETE_GOOD (completed before deadline)
`cb` :
COMPLETE_BAD (completed after deadline)
`ig` :
INCOMPLETE_GOOD (incomplete before deadline)
`ib` :
INCOMPLETE_BAD (incomplete after deadline) |
+| Attendance | `p` :
PRESENT `a` :
ABSENT `awr` :
ABSENT_WITH_REASON |
+| Tutorial | `as` :
ASSIGNED `av` :
AVAILABLE |
+
+### Tag Name
+Regardless of tag type, tag names must abide by the following constraints:
+- must be alphanumeric
+- no whitespace between words in the tag
+- tags attached to each person must have unique names (each person can only have one tag of a specific name, regardless of tag types. But tags of the same name can be attached to different contacts)
+
+Here are some recommended tag names for the various tag types.
+
+| Tag type | Examples of recommended tag names |
+|------------|-----------------------------------|
+| Assignment | `Assignment1` `MockPE` |
+| Attendance | `Week1` `Week2` |
+| Tutorial | `TUE08` `WED10` `THU09` |
+
+
+### Marking a tag : `mark`
+
+Updates the status of the specified tag with the specified status for the specified person(s). If the
+tag specified does not yet exist for the person(s), a new tag with the tag name and tag status will be
+created and attached to the person(s).
+
+
+
+:bulb: **Notes:**
+
+* The type of the tag(s) to be updated/created are specified through their tag status.
+* If a tag is to be marked with the status `cg`, `cb`, `ig` or `ib`, it would be identified as an assignment tag and displayed together with other assignment tags in the UI. Similarly for attendance and tutorial tags.
+* If a specific person(s) already has a tag with the same tag name as the tag that is to be marked, but his existing tag has a different tag type as the type identified by new status from the mark command, his original tag would then be replaced by the tag with new type and status but the same tag name.
+
+
+
+Format: `mark (all | INDEX [OTHER_INDICES...]) /t TAG [OTHER_TAGS...] /ts TAG_STATUS`
+
+* The index refers to the index number shown in the displayed person list.
+* The index **must be a positive integer** within the size of the displayed list.
+* When `all` is used, the command will apply to all persons in the displayed list.
+* When multiple `TAG`s are specified, the same `TAG_STATUS` will be applied to all the tags.
+* `TAG_STATUS` must be one of the [above specified values](#tag-status)
+
+Examples:
+* `mark 1 /t Assignment1 /ts cg` updates the `Assignment1` tag
+to
COMPLETE_GOOD for the 1st person in the displayed list if they already
+have the tag. The `Assignment1` tag of
COMPLETE_GOOD status would be added
+to the contact if they previously did not have the tag.
+* `mark 2 3 /t week1 week2 /ts awr` updates the `week1` and `week2` tags to
+
ABSENT_WITH_REASON for the 2nd and 3rd persons in the displayed list
+if they already have the tag. Both tags with specified status would be added to the two contacts if any of them
+previously did not have the tags.
+* `mark all /t TUE08 /ts as` updates the `TUE08` tag to
ASSIGNED to
+assign every person in the displayed list to the tutorial group TUE08 if they already have the tag. The `TUE08` tag
+with specified status would be added to any listed contact that previously did not have the tag.
+
+
+
+:information_source: **Note:**
+
+For **Tutorial** tags, the tutorial name must be that of a valid Tutorial tag in the
+list of available tutorial sessions defined with the [tuttag](#adding-a-tutorial-tuttag-add) command.
+For example, in the third example above, `TUE08` should be added as a tutorial tag first using [`tuttag add /t TUE08`](#adding-a-tutorial-tuttag-add).
+
+
+
+### Adding a Tutorial: `tuttag add`
+
+Adds the specified Tutorial tag to the list of valid Tutorial tags. Tutorial tags (identified by Tutorial tag names) not from the valid Tutorial tag list cannot be used.
+
+Format: `tuttag add /t TAG`
+
+Examples:
+
+* `tuttag add /t TUE08` adds TUE08 as a valid Tutorial tag.
+
+### Deleting a Tutorial: `tuttag del`
+
+Deletes the specified Tutorial tag from the valid Tutorial tag list. If the specified tag does not exist in the list, no change would happen.
+
+Warning: All persons with the Tutorial tag (identified by tag name, regardless of tag status) will also have the Tutorial tag removed should this command execute successfully.
+However, if the tag the person has is of the same specified tag name but of a tag type other than Tutorial (it is an Assignment or Attendance tag), this tag would not be removed.
+
+Format: `tuttag del /t TAG`
+
+Examples:
+
+* `tuttag del /t WED09` deletes WED09 from the valid Tutorial tag list, and removes the WED09 Tutorial tag from all persons.
+
+### Listing All Tutorials: `tuttag list`
+
+Lists all valid Tutorial tags in TrAcker.
+
+Format: `tuttag list`
+
+### Removing a tag: `removetag`
+
+Removes an individual tag of the specified tag name (regardless of tag type) from a person. If the specified tag does not exist, the person's tags would remain unchanged.
+
+Format: `removetag (all | INDEX [OTHER_INDICES...]) /t TAG`
+
+* The index refers to the index number shown in the displayed person list.
+* The index **must be a positive integer** within the size of the displayed list.
+* If the person does not have the specified tag, the command will leave the person unchanged.
+
+Examples:
+* `removetag 1 /t Assignment1` removes the `Assignment1` tag from the 1st person in the displayed list.
+* `removetag 2 3 /t Assignment2` removes the `Assignment2` tag from the 2nd and 3rd person in the displayed list.
+* `removetag all /t Assignment3` removes the `Assignment3` tag from every person in the displayed list.
+
+
+### Locating available TAs for a tutorial group: `available`
+
+Filters and lists all TAs who are available for (and not assigned to) a specified tutorial session to serve as replacement TAs.
+
+Format: `available /g TUTORIAL`
+
+
+
+:bulb: **Notes:**
+
+* Only one Tutorial tag name can be specified per `available` command.
+* Thus, after each `/g` flag, there can only be one Tutorial tag name specified, i.e. `available /g WED10 THU10` is an invalid input.
+* There can also only be one '/g' flag supplied per `available` command. If more than one `/g` flag is supplied within the same `available` command,
+only the tag name after the last `/g` will be processed, i.e. in `available /g WED10 /g THU10`, only `THU10
+will be taken in as a parameter while `WED10` will be ignored.
-### Archiving data files `[coming in v2.0]`
+* The search is case-sensitive and must match the specified tutorial group exactly.
+
+Examples:
+* `available /g TUE08` returns all TAs who are available for tutorial group `TUE08`
+
+### Viewing help : `help`
+
+Prompts a popup containing the link to the user guide.
-_Details coming soon ..._
+![help message](images/helpMessage.png)
+
+Format: `help`
--------------------------------------------------------------------------------------------------------------------
## 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.
+Welcome to our FAQ section! Here, we've gathered answers to the questions we hear most often from our users.
+If you can't find what you're looking for, feel free to reach out to us directly.
+
+**Q**: How do I transfer my data to another computer?
+**A**: Install the app in the other computer and overwrite the data file it creates with the file that
+contains the data of your previous TrAcker home folder.
--------------------------------------------------------------------------------------------------------------------
-## Known issues
+## Known issues
+
+:hammer: Heads-up, we're aware of a few hiccups that some users might be experiencing. Don't worry, our team is
+working hard to squash those bugs! Meanwhile, here are some workarounds for you:
-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.
+* **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.
--------------------------------------------------------------------------------------------------------------------
## 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`
+| Action | Format, Examples |
+|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
+| **Add** | add [stu | ta] /n NAME /i ID /p PHONE /e EMAIL
e.g., `add stu /n Alex Yeoh /i A0777777L /p 87438807 /e alexyeoh@example.com` |
+| **List** | list |
+| **Edit** | edit INDEX [/n NAME] [/p PHONE] [/e EMAIL]
e.g.,`edit 1 /p 91234567 /e johndoe@example.com` |
+| **Find** | find [stu | ta] [/n NAME] [/i ID] [/p PHONE] [/e EMAIL] [/t TAGS...]
e.g., `find /t wed assignment1` |
+| **Delete** | delete (all | INDEX [OTHER_INDICES...])
e.g., `delete 3` |
+| **Clear** | clear |
+| **Exit** | exit |
+| **Mark** | mark (all | INDEX [OTHER_INDICES...]) /t TAG [OTHER_TAGS...] /ts TAG_STATUS
e.g., `mark 1 /t Assignment1 /ts cg` |
+| **Create Valid Tutorial Tag** | tuttag add /t TAG
e.g., `tuttag add /t TUE08` |
+| **Delete Valid Tutorial Tag** | tuttag del /t TAG
e.g., `tuttag del /t WED09` |
+| **List Valid Tutorial Tags** | tuttag list |
+| **Remove Tag** | removetag INDEX /t TAG
e.g., `removetag 1 /t Assignment1` |
+| **Available** | available /g TUTORIAL
e.g., `available /g TUES08` |
+| **Help** | help |
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..0d64a21fd8d 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "TrAcker"
theme: minima
header_pages:
@@ -8,7 +8,7 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+repository: "AY2324S2-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..d6da95db4de 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: "TrAcker";
font-size: 32px;
}
}
diff --git a/docs/diagrams/AvailableActivityDiagram.puml b/docs/diagrams/AvailableActivityDiagram.puml
new file mode 100644
index 00000000000..792d9e80f8a
--- /dev/null
+++ b/docs/diagrams/AvailableActivityDiagram.puml
@@ -0,0 +1,21 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+start
+:User enters Available Command;
+
+:AvailableCommandParser parses input;
+
+if () then ([command is valid])
+ :Creates an AvailableCommand with a TutorialTagContainsGroupPredicate;
+ if () then ([tutorial group exists])
+ :Update FilterPersonList with the predicate;
+ else ([else])
+ :Display error message;
+ endif
+else ([command is invalid])
+ :Display error message;
+endif
+stop
+@enduml
diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml
index 598474a5c82..9357580c7b3 100644
--- a/docs/diagrams/BetterModelClassDiagram.puml
+++ b/docs/diagrams/BetterModelClassDiagram.puml
@@ -5,17 +5,18 @@ skinparam arrowColor MODEL_COLOR
skinparam classBackgroundColor MODEL_COLOR
AddressBook *-right-> "1" UniquePersonList
-AddressBook *-right-> "1" UniqueTagList
-UniqueTagList -[hidden]down- UniquePersonList
-UniqueTagList -[hidden]down- UniquePersonList
+AddressBook *-right-> "1" UniqueTutorialTagList
+UniqueTutorialTagList -[hidden]down- UniquePersonList
+UniqueTutorialTagList -[hidden]down- UniquePersonList
-UniqueTagList -right-> "*" Tag
+UniqueTutorialTagList -right-> "*" Tag
UniquePersonList -right-> Person
Person -up-> "*" Tag
Person *--> Name
+Person *--> PersonType
+Person *--> ID
Person *--> Phone
Person *--> Email
-Person *--> Address
@enduml
diff --git a/docs/diagrams/FindSequenceDiagram.puml b/docs/diagrams/FindSequenceDiagram.puml
new file mode 100644
index 00000000000..ceb3abb0ab8
--- /dev/null
+++ b/docs/diagrams/FindSequenceDiagram.puml
@@ -0,0 +1,70 @@
+@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 ":FindCommandParser" as FindCommandParser LOGIC_COLOR
+participant "d:FindCommand" as FindCommand LOGIC_COLOR
+participant "r:CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant "m:Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("find /n John /i 6Z")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("find /n John /i 6Z")
+activate AddressBookParser
+
+create FindCommandParser
+AddressBookParser -> FindCommandParser
+activate FindCommandParser
+
+FindCommandParser --> AddressBookParser
+deactivate FindCommandParser
+
+AddressBookParser -> FindCommandParser : parse("/n John /i 6Z")
+activate FindCommandParser
+
+create FindCommand
+FindCommandParser -> FindCommand
+activate FindCommand
+
+FindCommand --> FindCommandParser :
+deactivate FindCommand
+
+FindCommandParser --> AddressBookParser : d
+deactivate FindCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+FindCommandParser -[hidden]-> AddressBookParser
+destroy FindCommandParser
+
+AddressBookParser --> LogicManager : d
+deactivate AddressBookParser
+
+LogicManager -> FindCommand : execute(m)
+activate FindCommand
+
+FindCommand -> Model : persistentUpdateFilteredList(Predicate)
+activate Model
+
+Model --> FindCommand
+deactivate Model
+
+create CommandResult
+FindCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> FindCommand
+deactivate CommandResult
+
+FindCommand --> LogicManager : r
+deactivate FindCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/MarkSequenceDiagram.puml b/docs/diagrams/MarkSequenceDiagram.puml
new file mode 100644
index 00000000000..b4ff25d0c95
--- /dev/null
+++ b/docs/diagrams/MarkSequenceDiagram.puml
@@ -0,0 +1,70 @@
+@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 ":MarkCommandParser" as MarkCommandParser LOGIC_COLOR
+participant "p:MarkCommand" as MarkCommand LOGIC_COLOR
+participant "r:CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant "m:Model" as Model MODEL_COLOR
+end box
+
+[-> LogicManager : execute("mark 1 /t Assignment1 /ts cg")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("mark 1 /t Assignment1 /ts cg")
+activate AddressBookParser
+
+create MarkCommandParser
+AddressBookParser -> MarkCommandParser
+activate MarkCommandParser
+
+MarkCommandParser --> AddressBookParser
+deactivate MarkCommandParser
+
+AddressBookParser -> MarkCommandParser : parse("1 /t Assignment1 /ts cg")
+activate MarkCommandParser
+
+create MarkCommand
+MarkCommandParser -> MarkCommand
+activate MarkCommand
+
+MarkCommand --> MarkCommandParser :
+deactivate MarkCommand
+
+MarkCommandParser --> AddressBookParser : p
+deactivate MarkCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+MarkCommandParser -[hidden]-> AddressBookParser
+destroy MarkCommandParser
+
+AddressBookParser --> LogicManager : p
+deactivate AddressBookParser
+
+LogicManager -> MarkCommand : execute(m)
+activate MarkCommand
+
+MarkCommand -> Model : setPerson(personToEdit, editedPerson)
+activate Model
+
+Model --> MarkCommand
+deactivate Model
+
+create CommandResult
+MarkCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> MarkCommand
+deactivate CommandResult
+
+MarkCommand --> LogicManager : r
+deactivate MarkCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml
index 0de5673070d..86fde05cffb 100644
--- a/docs/diagrams/ModelClassDiagram.puml
+++ b/docs/diagrams/ModelClassDiagram.puml
@@ -14,9 +14,10 @@ Class UserPrefs
Class UniquePersonList
Class Person
-Class Address
+Class PersonType
Class Email
Class Name
+Class ID
Class Phone
Class Tag
@@ -37,18 +38,22 @@ UserPrefs .up.|> ReadOnlyUserPrefs
AddressBook *--> "1" UniquePersonList
UniquePersonList --> "~* all" Person
+
+Person *--> PersonType
Person *--> Name
+Person *--> ID
Person *--> Phone
Person *--> Email
-Person *--> Address
Person *--> "*" Tag
Person -[hidden]up--> I
UniquePersonList -[hidden]right-> I
-Name -[hidden]right-> Phone
-Phone -[hidden]right-> Address
-Address -[hidden]right-> Email
+PersonType -[hidden]right-> Name
+Name -[hidden]right-> ID
+ID -[hidden]right-> Phone
+
+
ModelManager --> "~* filtered" Person
@enduml
diff --git a/docs/diagrams/TaggingClassDiagram.puml b/docs/diagrams/TaggingClassDiagram.puml
new file mode 100644
index 00000000000..ccd03714e5d
--- /dev/null
+++ b/docs/diagrams/TaggingClassDiagram.puml
@@ -0,0 +1,27 @@
+@startuml
+!include style.puml
+skinparam arrowThickness 1.1
+skinparam arrowColor LOGIC_COLOR_T4
+skinparam classBackgroundColor LOGIC_COLOR
+
+package "Tagging"{
+Class "{abstract}\nPerson" as Person
+Class Student
+Class Ta
+Class "{abstract}\nTag" as Tag
+Class AssignmentTag
+Class AttendanceTag
+Class TutorialTag
+}
+
+Student -up-|> Person
+Ta -up-|> Person
+Student --> "*" AssignmentTag
+Student --> "*" AttendanceTag
+Student --> "*" TutorialTag
+Ta --> "*" TutorialTag
+
+AssignmentTag -up--|> Tag
+AttendanceTag -up--|> Tag
+TutorialTag -up---|> Tag
+@enduml
diff --git a/docs/diagrams/TuttagActivityDiagram.puml b/docs/diagrams/TuttagActivityDiagram.puml
new file mode 100644
index 00000000000..89f105f0535
--- /dev/null
+++ b/docs/diagrams/TuttagActivityDiagram.puml
@@ -0,0 +1,23 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+start
+:User enters "tuttag add /t TUE08";
+
+:EditTutTagListCommandParser parses input;
+
+if () then ([command is valid])
+ :Updates UniqueTutorialTagList with new TutorialTag;;
+ :User enters "mark all /t TUE08 /ts as";
+ :MarkCommandParser parses input;
+ if () then ([command is valid])
+ :Every person in displayed list gets "TUE08" TutorialTag with TagStatus "ASSIGNED";
+ else ([command is invalid])
+ :Display error message;
+ endif
+else ([command is invalid])
+ :Display error message;
+endif
+stop
+@enduml
diff --git a/docs/images/AvailableActivityDiagram.png b/docs/images/AvailableActivityDiagram.png
new file mode 100644
index 00000000000..5fa59f13c97
Binary files /dev/null and b/docs/images/AvailableActivityDiagram.png differ
diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png
index 02a42e35e76..c9154416e95 100644
Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ
diff --git a/docs/images/FindSequenceDiagram.png b/docs/images/FindSequenceDiagram.png
new file mode 100644
index 00000000000..d4cd021baa7
Binary files /dev/null and b/docs/images/FindSequenceDiagram.png differ
diff --git a/docs/images/MarkSequenceDiagram.png b/docs/images/MarkSequenceDiagram.png
new file mode 100644
index 00000000000..705440e78d9
Binary files /dev/null and b/docs/images/MarkSequenceDiagram.png differ
diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png
index a19fb1b4ac8..958c84eb7f2 100644
Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ
diff --git a/docs/images/TaggingClassDiagram.png b/docs/images/TaggingClassDiagram.png
new file mode 100644
index 00000000000..fa7c2e0bd6d
Binary files /dev/null and b/docs/images/TaggingClassDiagram.png differ
diff --git a/docs/images/TuttagActivityDiagram.png b/docs/images/TuttagActivityDiagram.png
new file mode 100644
index 00000000000..c349c0602e3
Binary files /dev/null and b/docs/images/TuttagActivityDiagram.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..36af73f68e9 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/findTaResult.png b/docs/images/findTaResult.png
new file mode 100644
index 00000000000..ec969ba17d1
Binary files /dev/null and b/docs/images/findTaResult.png differ
diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png
index b1f70470137..691fd12d2fe 100644
Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ
diff --git a/docs/images/kaitinghh.png b/docs/images/kaitinghh.png
new file mode 100644
index 00000000000..e278caa301a
Binary files /dev/null and b/docs/images/kaitinghh.png differ
diff --git a/docs/images/wang-xinrong.png b/docs/images/wang-xinrong.png
new file mode 100644
index 00000000000..4f8e7806739
Binary files /dev/null and b/docs/images/wang-xinrong.png differ
diff --git a/docs/images/wongkj12.png b/docs/images/wongkj12.png
new file mode 100644
index 00000000000..6b9544e634a
Binary files /dev/null and b/docs/images/wongkj12.png differ
diff --git a/docs/images/yongkotaro.png b/docs/images/yongkotaro.png
new file mode 100644
index 00000000000..3d299becb4a
Binary files /dev/null and b/docs/images/yongkotaro.png differ
diff --git a/docs/images/yyccbb.png b/docs/images/yyccbb.png
new file mode 100644
index 00000000000..ee832261d44
Binary files /dev/null and b/docs/images/yyccbb.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..a9e038ab5cf 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,6 +1,6 @@
---
layout: page
-title: AddressBook Level-3
+title: TrAcker
---
[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
@@ -8,10 +8,12 @@ title: AddressBook Level-3
![Ui](images/Ui.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).
+**TrAcker 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).
-* 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 **TrAcker**, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.
+ html#quick-start).
+* If you are interested about developing **TrAcker**, the [**Developer Guide**](DeveloperGuide.html) is a good place to
+ start.
**Acknowledgements**
diff --git a/docs/team/kaitinghh.md b/docs/team/kaitinghh.md
new file mode 100644
index 00000000000..a113a026ee6
--- /dev/null
+++ b/docs/team/kaitinghh.md
@@ -0,0 +1,46 @@
+---
+layout: page
+title: Ho Kai Ting'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/wangxinrong.md b/docs/team/wangxinrong.md
new file mode 100644
index 00000000000..6794ac53041
--- /dev/null
+++ b/docs/team/wangxinrong.md
@@ -0,0 +1,44 @@
+---
+layout: page
+title: Wang Xinrong'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
diff --git a/docs/team/wongkj12.md b/docs/team/wongkj12.md
new file mode 100644
index 00000000000..4c242ce1785
--- /dev/null
+++ b/docs/team/wongkj12.md
@@ -0,0 +1,46 @@
+---
+layout: page
+title: Kai Jie'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/johndoe.md b/docs/team/yyccbb.md
similarity index 100%
rename from docs/team/johndoe.md
rename to docs/team/yyccbb.md
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 3d6bd06d5af..c62badc0a91 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/address/MainApp.java
@@ -12,9 +12,10 @@
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.commons.util.StatefulStringUtil;
import seedu.address.logic.Logic;
import seedu.address.logic.LogicManager;
+import seedu.address.logic.parser.StatefulParserUtil;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
@@ -44,6 +45,7 @@ public class MainApp extends Application {
protected Logic logic;
protected Storage storage;
protected Model model;
+ protected StatefulStringUtil statefulStringUtil;
protected Config config;
@Override
@@ -65,6 +67,10 @@ public void init() throws Exception {
logic = new LogicManager(model, storage);
ui = new UiManager(logic);
+
+ StatefulStringUtil.initialize(model);
+
+ StatefulParserUtil.initialize(model);
}
/**
@@ -83,6 +89,8 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
logger.info("Creating a new data file " + storage.getAddressBookFilePath()
+ " populated with a sample AddressBook.");
}
+ // need to check the initial data against the tutorial tag list to remove the invalid TutoiralTags
+ // (TutorialTags that are not in the UniqueTutorialTagList yet have been attached to contact entries)
initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
} catch (DataLoadingException e) {
logger.warning("Data file at " + storage.getAddressBookFilePath() + " could not be loaded."
@@ -131,7 +139,7 @@ protected Config initConfig(Path configFilePath) {
try {
ConfigUtil.saveConfig(initializedConfig, configFilePathUsed);
} catch (IOException e) {
- logger.warning("Failed to save config file : " + StringUtil.getDetails(e));
+ logger.warning("Failed to save config file : " + StatefulStringUtil.getDetails(e));
}
return initializedConfig;
}
@@ -162,7 +170,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
try {
storage.saveUserPrefs(initializedPrefs);
} catch (IOException e) {
- logger.warning("Failed to save config file : " + StringUtil.getDetails(e));
+ logger.warning("Failed to save config file : " + StatefulStringUtil.getDetails(e));
}
return initializedPrefs;
@@ -180,7 +188,7 @@ public void stop() {
try {
storage.saveUserPrefs(model.getUserPrefs());
} catch (IOException e) {
- logger.severe("Failed to save preferences " + StringUtil.getDetails(e));
+ logger.severe("Failed to save preferences " + StatefulStringUtil.getDetails(e));
}
}
}
diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/address/commons/core/index/Index.java
index dd170d8b68d..17cedf18e5c 100644
--- a/src/main/java/seedu/address/commons/core/index/Index.java
+++ b/src/main/java/seedu/address/commons/core/index/Index.java
@@ -47,6 +47,11 @@ public static Index fromOneBased(int oneBasedIndex) {
return new Index(oneBasedIndex - 1);
}
+ @Override
+ public int hashCode() {
+ return zeroBasedIndex;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -57,9 +62,8 @@ public boolean equals(Object other) {
if (!(other instanceof Index)) {
return false;
}
-
Index otherIndex = (Index) other;
- return zeroBasedIndex == otherIndex.zeroBasedIndex;
+ return this.zeroBasedIndex == otherIndex.zeroBasedIndex;
}
@Override
diff --git a/src/main/java/seedu/address/commons/util/StatefulStringUtil.java b/src/main/java/seedu/address/commons/util/StatefulStringUtil.java
new file mode 100644
index 00000000000..97d3898c32b
--- /dev/null
+++ b/src/main/java/seedu/address/commons/util/StatefulStringUtil.java
@@ -0,0 +1,149 @@
+package seedu.address.commons.util;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+
+import javafx.collections.ObservableList;
+import seedu.address.model.Model;
+import seedu.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TutorialTag;
+
+/**
+ * Helper functions for handling strings.
+ */
+public class StatefulStringUtil {
+ private static StatefulStringUtil instance = null;
+ private Model model;
+
+ private StatefulStringUtil(Model model) {
+ this.model = model;
+ }
+
+ /**
+ * Initializes the StatefulStringUtil instance.
+ */
+ public static void initialize(Model model) {
+ if (instance == null) {
+ instance = new StatefulStringUtil(model);
+ }
+ }
+
+ public static StatefulStringUtil getInstance() {
+ if (instance == null) {
+ throw new IllegalStateException("StatefulStringUtil has not been initialized");
+ }
+ return instance;
+ }
+
+ /**
+ * Returns true if the {@code sentence} contains the {@code word}.
+ * Ignores case, and performs subword matching.
+ *
examples:
+ * containsWordIgnoreCase("ABc def", "abc") == true
+ * containsWordIgnoreCase("ABc def", "DEF") == true
+ * containsWordIgnoreCase("ABc def", "AB") == true
+ * containsWordIgnoreCase("ABc def", "Ac") == false //not a match
+ *
+ * @param sentence cannot be null
+ * @param word cannot be null, cannot be empty
+ */
+ public static boolean containsSubwordIgnoreCase(String sentence, String word) {
+ requireNonNull(sentence);
+ requireNonNull(word);
+
+ String preppedWord = word.trim().toLowerCase();
+ checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty");
+ checkArgument(preppedWord.split("\\s+").length == 1, "Word parameter should be a single word");
+
+ String preppedSentence = sentence;
+ String[] wordsInPreppedSentence = preppedSentence.split("\\s+");
+
+ return Arrays.stream(wordsInPreppedSentence)
+ .anyMatch(wordInPreppedSentence -> wordInPreppedSentence.toLowerCase().contains(preppedWord));
+ }
+
+ /**
+ * Returns true if the {@code tag} contains the {@code word}.
+ * Ignores case, and performs subword matching if {@code word} is a subword of a valid tutorialtag,
+ * else it performs a full word match.
+ * @param tag cannot be null
+ * @param word cannot be null, cannot be empty
+ */
+ public static boolean tagContainsWordIgnoreCase(Tag tag, String word) {
+ requireNonNull(tag);
+ requireNonNull(word);
+
+ String tagName = tag.getTagName().toLowerCase();
+ String preppedWord = word.trim();
+ checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty");
+ ObservableList
validTutorials =
+ StatefulStringUtil.getInstance().model.getTutorialTagList();
+
+ for (TutorialTag tutorial : validTutorials) {
+ String tutorialGroup = tutorial.getTagName().toLowerCase();
+ if (tutorialGroup.contains(preppedWord)) {
+ return tagName.contains(preppedWord) && tag.isTutorial();
+ }
+ }
+ return tagName.equalsIgnoreCase(preppedWord);
+ }
+
+ /**
+ * Returns true if the {@code tag} contains the {@code tutorialGroup}.
+ * Ignores case, and performs a full word match.
+ * @param tag cannot be null
+ * @param tutorialGroup cannot be null, cannot be empty
+ */
+ public static boolean containsTutorialGroup(Tag tag, String tutorialGroup) {
+ requireNonNull(tag);
+ requireNonNull(tutorialGroup);
+
+ String tagName = tag.getTagName();
+ checkArgument(!tutorialGroup.isEmpty(), "Tutorial group parameter cannot be empty");
+ checkArgument(!tutorialGroup.contains(" "), "Only use one word for tutorial group parameter");
+
+ ObservableList validTutorials =
+ StatefulStringUtil.getInstance().model.getTutorialTagList();
+
+ for (TutorialTag tutorial : validTutorials) {
+ String validTutorialGroupTag = tutorial.getTagName();
+ if (validTutorialGroupTag.equalsIgnoreCase(tutorialGroup)) {
+ return tagName.equalsIgnoreCase(tutorialGroup) && tag.getTagStatus() == TagStatus.AVAILABLE;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns a detailed message of the t, including the stack trace.
+ */
+ public static String getDetails(Throwable t) {
+ requireNonNull(t);
+ StringWriter sw = new StringWriter();
+ t.printStackTrace(new PrintWriter(sw));
+ return t.getMessage() + "\n" + sw.toString();
+ }
+
+ /**
+ * Returns true if {@code s} represents a non-zero unsigned integer
+ * e.g. 1, 2, 3, ..., {@code Integer.MAX_VALUE}
+ * Will return false for any other non-null string input
+ * e.g. empty string, "-1", "0", "+1", and " 2 " (untrimmed), "3 0" (contains whitespace), "1 a" (contains letters)
+ * @throws NullPointerException if {@code s} is null.
+ */
+ public static boolean isNonZeroUnsignedInteger(String s) {
+ requireNonNull(s);
+
+ try {
+ int value = Integer.parseInt(s);
+ return value > 0 && !s.startsWith("+"); // "+1" is successfully parsed by Integer#parseInt(String)
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/commons/util/StringListUtil.java b/src/main/java/seedu/address/commons/util/StringListUtil.java
new file mode 100644
index 00000000000..6d41c12f912
--- /dev/null
+++ b/src/main/java/seedu/address/commons/util/StringListUtil.java
@@ -0,0 +1,30 @@
+package seedu.address.commons.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This ListUtil class contains additional methods for list manipulation.
+ */
+public class StringListUtil {
+
+ /**
+ * Given a list of strings, returns a new list of strings separated by white spaces.
+ * @param originalList list of strings
+ * @return separatedList a new list of strings separated by white spaces
+ */
+ public static List separateWithSpaces(List originalList) {
+ List separatedList = new ArrayList<>();
+ for (String str : originalList) {
+ if (str.contains(" ")) {
+ String[] parts = str.split("\\s+");
+ for (String part : parts) {
+ separatedList.add(part);
+ }
+ } else {
+ separatedList.add(str);
+ }
+ }
+ return separatedList;
+ }
+}
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java
deleted file mode 100644
index 61cc8c9a1cb..00000000000
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package seedu.address.commons.util;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.commons.util.AppUtil.checkArgument;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Arrays;
-
-/**
- * Helper functions for handling strings.
- */
-public class StringUtil {
-
- /**
- * Returns true if the {@code sentence} contains the {@code word}.
- * Ignores case, but a full word match is required.
- * examples:
- * containsWordIgnoreCase("ABc def", "abc") == true
- * containsWordIgnoreCase("ABc def", "DEF") == true
- * containsWordIgnoreCase("ABc def", "AB") == false //not a full word match
- *
- * @param sentence cannot be null
- * @param word cannot be null, cannot be empty, must be a single word
- */
- public static boolean containsWordIgnoreCase(String sentence, String word) {
- requireNonNull(sentence);
- requireNonNull(word);
-
- String preppedWord = word.trim();
- checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty");
- checkArgument(preppedWord.split("\\s+").length == 1, "Word parameter should be a single word");
-
- String preppedSentence = sentence;
- String[] wordsInPreppedSentence = preppedSentence.split("\\s+");
-
- return Arrays.stream(wordsInPreppedSentence)
- .anyMatch(preppedWord::equalsIgnoreCase);
- }
-
- /**
- * Returns a detailed message of the t, including the stack trace.
- */
- public static String getDetails(Throwable t) {
- requireNonNull(t);
- StringWriter sw = new StringWriter();
- t.printStackTrace(new PrintWriter(sw));
- return t.getMessage() + "\n" + sw.toString();
- }
-
- /**
- * Returns true if {@code s} represents a non-zero unsigned integer
- * e.g. 1, 2, 3, ..., {@code Integer.MAX_VALUE}
- * Will return false for any other non-null string input
- * e.g. empty string, "-1", "0", "+1", and " 2 " (untrimmed), "3 0" (contains whitespace), "1 a" (contains letters)
- * @throws NullPointerException if {@code s} is null.
- */
- public static boolean isNonZeroUnsignedInteger(String s) {
- requireNonNull(s);
-
- try {
- int value = Integer.parseInt(s);
- return value > 0 && !s.startsWith("+"); // "+1" is successfully parsed by Integer#parseInt(String)
- } catch (NumberFormatException nfe) {
- return false;
- }
- }
-}
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..95cbb436453 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -4,6 +4,7 @@
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
@@ -16,12 +17,14 @@
public interface Logic {
/**
* Executes the command and returns the result.
- * @param commandText The command as entered by the user.
+ * @param command The requested command.
* @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;
+ CommandResult execute(Command command) throws CommandException, ParseException;
+
+ Command parseCommand(String commandText) throws CommandException, ParseException;
/**
* Returns the AddressBook.
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 5aa3b91c7d0..7a2b5d45249 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -43,11 +43,15 @@ public LogicManager(Model model, Storage storage) {
}
@Override
- public CommandResult execute(String commandText) throws CommandException, ParseException {
+ public Command parseCommand(String commandText) throws CommandException, ParseException {
logger.info("----------------[USER COMMAND][" + commandText + "]");
+ Command command = addressBookParser.parseCommand(commandText);
+ return command;
+ }
+ @Override
+ public CommandResult execute(Command command) throws CommandException, ParseException {
CommandResult commandResult;
- Command command = addressBookParser.parseCommand(commandText);
commandResult = command.execute(model);
try {
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
index ecd32c31b53..8251d08d1fb 100644
--- a/src/main/java/seedu/address/logic/Messages.java
+++ b/src/main/java/seedu/address/logic/Messages.java
@@ -6,6 +6,8 @@
import seedu.address.logic.parser.Prefix;
import seedu.address.model.person.Person;
+import seedu.address.model.person.PersonType;
+import seedu.address.model.tag.Tag;
/**
* Container for user visible messages.
@@ -14,10 +16,18 @@ public class Messages {
public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
- public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
+ public static final String MESSAGE_INVALID_PERSON_TYPE = PersonType.MESSAGE_CONSTRAINTS;
+ public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided %1$s is invalid";
public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
+ public static final String MESSAGE_PERSON_LISTED_OVERVIEW = "%1$d person listed!";
public static final String MESSAGE_DUPLICATE_FIELDS =
"Multiple values specified for the following single-valued field(s): ";
+ public static final String MESSAGE_INVALID_TUTORIAL_TAG_VALUE =
+ "Specified tutorial tag name is not allowed: ";
+
+ public static final String MESSAGE_AVAILABLE_TAS_OVERVIEW = "%1$d TAs available for the tutorial group.";
+
+ public static final String MESSAGE_INVALID_TAG_NAME = Tag.MESSAGE_CONSTRAINTS;
/**
* Returns an error message indicating the duplicate prefixes.
@@ -37,13 +47,16 @@ public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePref
public static String format(Person person) {
final StringBuilder builder = new StringBuilder();
builder.append(person.getName())
+ .append("; ID: ")
+ .append(person.getId())
.append("; Phone: ")
.append(person.getPhone())
.append("; Email: ")
- .append(person.getEmail())
- .append("; Address: ")
- .append(person.getAddress())
- .append("; Tags: ");
+ .append(person.getEmail());
+ if (person.getTags().isEmpty()) {
+ return builder.toString();
+ }
+ builder.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
index 5d7185a9680..c88fde106ac 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddCommand.java
@@ -1,11 +1,12 @@
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_ID;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.CliSyntax.TYPE_STU;
+import static seedu.address.logic.parser.CliSyntax.TYPE_TA;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
@@ -20,20 +21,19 @@ 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. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a Student / TA to TrAcker.\n"
+ "Parameters: "
- + PREFIX_NAME + "NAME "
- + PREFIX_PHONE + "PHONE "
- + PREFIX_EMAIL + "EMAIL "
- + PREFIX_ADDRESS + "ADDRESS "
- + "[" + PREFIX_TAG + "TAG]...\n"
+ + "[" + TYPE_STU + " | " + TYPE_TA + "] "
+ + PREFIX_NAME + " NAME "
+ + PREFIX_ID + " ID "
+ + PREFIX_PHONE + " PHONE "
+ + PREFIX_EMAIL + " EMAIL \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";
+ + TYPE_STU + " "
+ + PREFIX_NAME + " John Doe "
+ + PREFIX_ID + " A0123456Z "
+ + PREFIX_PHONE + " 98765432 "
+ + PREFIX_EMAIL + " johnd@example.com ";
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";
diff --git a/src/main/java/seedu/address/logic/commands/AvailableCommand.java b/src/main/java/seedu/address/logic/commands/AvailableCommand.java
new file mode 100644
index 00000000000..4ccd0eb4af2
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AvailableCommand.java
@@ -0,0 +1,63 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.TutorialTagContainsGroupPredicate;
+import seedu.address.model.tag.TutorialTag;
+
+/**
+ * Finds and lists all available TAs for the tutorial group.
+ */
+public class AvailableCommand extends Command {
+ public static final String COMMAND_WORD = "available";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all available TAs for the tutorial slot.\n"
+ + "Parameters: /g TUTORIAL \n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_GROUP + " TUES08 ";
+
+ public static final String MESSAGE_NON_EMPTY_GROUP_NAME = "Tutorial group name parameter cannot be empty";
+ public static final String SAMPLE_COMMAND = COMMAND_WORD + " " + PREFIX_GROUP + " WED10 ";
+
+ private final TutorialTagContainsGroupPredicate predicate;
+ private final TutorialTag tutorialGroup;
+
+ /**
+ * Creates an AvailableCommand to find all available TAs for the tutorial group.
+ */
+ public AvailableCommand(TutorialTagContainsGroupPredicate predicate, TutorialTag tutorialGroup) {
+ this.predicate = predicate;
+ this.tutorialGroup = tutorialGroup;
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (!model.hasTutorialTag(tutorialGroup)) {
+ throw new CommandException(Messages.MESSAGE_INVALID_TUTORIAL_TAG_VALUE + tutorialGroup.getTagName());
+ }
+
+ model.updateFilteredPersonList(predicate);
+ return new CommandResult(
+ String.format(Messages.MESSAGE_AVAILABLE_TAS_OVERVIEW, model.getFilteredPersonList().size()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AvailableCommand)) {
+ return false;
+ }
+
+ AvailableCommand otherAvailableCommand = (AvailableCommand) other;
+ return predicate.equals(otherAvailableCommand.predicate);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
index 9c86b1fa6e4..2564621acfd 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java
@@ -13,7 +13,9 @@ public class ClearCommand extends Command {
public static final String COMMAND_WORD = "clear";
public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
-
+ public ClearCommand() {
+ this.needsWarningPopup = true;
+ }
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java
index 64f18992160..1fdaec0c931 100644
--- a/src/main/java/seedu/address/logic/commands/Command.java
+++ b/src/main/java/seedu/address/logic/commands/Command.java
@@ -7,6 +7,7 @@
* Represents a command with hidden internal logic and the ability to be executed.
*/
public abstract class Command {
+ protected boolean needsWarningPopup = false;
/**
* Executes the command and returns the result message.
@@ -16,5 +17,8 @@ public abstract class Command {
* @throws CommandException If an error occurs during command execution.
*/
public abstract CommandResult execute(Model model) throws CommandException;
+ public boolean getNeedsWarningPopup() {
+ return needsWarningPopup;
+ }
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 1135ac19b74..0e07e7cc493 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -1,8 +1,11 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.ToStringBuilder;
@@ -19,30 +22,81 @@ 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"
- + "Parameters: INDEX (must be a positive integer)\n"
- + "Example: " + COMMAND_WORD + " 1";
+ + ": Deletes the person(s) identified by the index number used in the displayed person list.\n"
+ + "Parameters: (all | INDEX (must be a positive integer within the size of the displayed list) "
+ + "[OTHER_INDICES]) \n"
+ + "Example 1: " + COMMAND_WORD + " 1\n"
+ + "Example 2: " + COMMAND_WORD + " 1 5 3";
public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
- private final Index targetIndex;
+ private final Set indices;
- public DeleteCommand(Index targetIndex) {
- this.targetIndex = targetIndex;
+ /**
+ * Constructor for the delete command.
+ * @param indices set of indices whose contact entry are to be deleted.
+ */
+ public DeleteCommand(Set indices) {
+ requireNonNull(indices);
+ this.needsWarningPopup = true;
+ this.indices = indices;
}
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
+ // need not check for duplicate indices because a Set is used
+ List lastShownList = new ArrayList<>(model.getFilteredPersonList());
+
+ StringBuilder resultStringBuilder;
+
+ checksInvalidIndices(lastShownList, indices);
+ try {
+ resultStringBuilder = indices.stream().map(x -> {
+ try {
+ return executeHelper(model, lastShownList, x);
+ } catch (CommandException e) {
+ throw new RuntimeException(e);
+ }
+ }).reduce(StringBuilder::append).orElse(new StringBuilder());
+ } catch (RuntimeException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof CommandException) {
+ throw (CommandException) cause;
+ } else {
+ throw e;
+ }
+ }
- if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ resultStringBuilder.deleteCharAt(resultStringBuilder.length() - 1); // remove the last '\n'
+ return new CommandResult(resultStringBuilder.toString());
+ }
+ private void checksInvalidIndices(List lastShownList, Set indices) throws CommandException {
+ // check whether index specified is within valid range
+ List invalidIndices = new ArrayList<>();
+ for (Index index: indices) {
+ if (index.getZeroBased() >= lastShownList.size()) {
+ invalidIndices.add(index.getOneBased());
+ }
+ }
+ if (invalidIndices.size() > 0) {
+ throw new CommandException(
+ String.format(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX, invalidIndices));
}
+ }
+
+ /**
+ * Deletes a single {@code Person}. Index is guaranteed to be a valid index.
+ */
+ private StringBuilder executeHelper(Model model, List lastShownList, Index index) throws CommandException {
+ requireAllNonNull(model, lastShownList, index);
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
+ Person personToDelete = lastShownList.get(index.getZeroBased());
model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
+
+ return new StringBuilder()
+ .append(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)))
+ .append("\n");
}
@Override
@@ -57,13 +111,13 @@ public boolean equals(Object other) {
}
DeleteCommand otherDeleteCommand = (DeleteCommand) other;
- return targetIndex.equals(otherDeleteCommand.targetIndex);
+ return indices.equals(otherDeleteCommand.indices);
}
@Override
public String toString() {
return new ToStringBuilder(this)
- .add("targetIndex", targetIndex)
+ .add("index", indices.toString())
.toString();
}
}
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
index 4b581c7331e..a8abc075c89 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/EditCommand.java
@@ -1,15 +1,11 @@
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;
@@ -21,10 +17,11 @@
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.Id;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
+import seedu.address.model.person.PersonType;
import seedu.address.model.person.Phone;
import seedu.address.model.tag.Tag;
@@ -35,18 +32,16 @@ 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 "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the particulars 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"
+ + "Parameters: INDEX (must be a positive integer within the size of the displayed list) "
+ + "[" + PREFIX_NAME + " NAME] "
+ + "[" + PREFIX_PHONE + " PHONE] "
+ + "[" + PREFIX_EMAIL + " EMAIL]\n"
+ "Example: " + COMMAND_WORD + " 1 "
- + PREFIX_PHONE + "91234567 "
- + PREFIX_EMAIL + "johndoe@example.com";
+ + 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.";
@@ -95,13 +90,15 @@ public CommandResult execute(Model model) throws CommandException {
private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) {
assert personToEdit != null;
+ PersonType currentType = personToEdit.getType();
Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
+ Id currentId = personToEdit.getId();
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());
+ Set currentTags = personToEdit.getTags();
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
+ return Person.of(currentType, updatedName, currentId, updatedPhone, updatedEmail,
+ currentTags);
}
@Override
@@ -136,8 +133,6 @@ public static class EditPersonDescriptor {
private Name name;
private Phone phone;
private Email email;
- private Address address;
- private Set tags;
public EditPersonDescriptor() {}
@@ -149,15 +144,13 @@ 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);
+ return CollectionUtil.isAnyNonNull(name, phone, email);
}
public void setName(Name name) {
@@ -184,31 +177,6 @@ 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) {
@@ -223,9 +191,7 @@ public boolean equals(Object other) {
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);
+ && Objects.equals(email, otherEditPersonDescriptor.email);
}
@Override
@@ -234,8 +200,6 @@ public String toString() {
.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/EditTutTagListCommand.java b/src/main/java/seedu/address/logic/commands/EditTutTagListCommand.java
new file mode 100644
index 00000000000..745479a4211
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/EditTutTagListCommand.java
@@ -0,0 +1,146 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+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.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TutorialTag;
+
+/**
+ * Finds and lists all persons in address book whose name contains any of the argument keywords.
+ * Keyword matching is case insensitive.
+ */
+public class EditTutTagListCommand extends Command {
+
+ /**
+ * Represents the subtype of the EditTutTagListCommand. ADD represents command to add an available
+ * tutorial tag, DELETE represents command to delete an available tutorial tag and LIST represents
+ * command to list all available tutorial tags.
+ */
+ public enum CommandSubtype { ADD, DELETE, LIST };
+
+ public static final String COMMAND_WORD = "tuttag";
+ public static final String ADD_FLAG = "add";
+ public static final String DELETE_FLAG = "del";
+ public static final String LIST_FLAG = "list";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Creates a TutorialTag to be used with the specified "
+ + "TagName.\n"
+ + "Parameters: MODE (must be 'add' for adding tags, 'del' for deleting tags or 'list' for listing "
+ + "existing tags) "
+ + PREFIX_TAG + " TAGNAME\n"
+ + "Example: " + COMMAND_WORD + " " + ADD_FLAG + " " + PREFIX_TAG + " THU10\n";
+
+ public static final String SAMPLE_COMMAND = COMMAND_WORD + " " + ADD_FLAG + " " + PREFIX_TAG + " WED10";
+ public static final String MESSAGE_SUCCESS = "New tutorial tag added: %1$s";
+ public static final String MESSAGE_DUPLICATE_TUTORIALTAG = "This tutorial tag already exists in the address book";
+ public static final String EMPTY_TUTORIALTAGLIST_OUTPUT = "Available Tutorial Tag(s): [ ]";
+ private final String tagName;
+ private final CommandSubtype commandType;
+
+ /**
+ * Creates a new EditTutTagListCommand.
+ *
+ * @param tagName TagName of the tutorial tag to be added or deleted.
+ * @param commandType indicates the command subtype.
+ */
+ public EditTutTagListCommand(String tagName, CommandSubtype commandType) {
+ this.tagName = tagName;
+ this.commandType = commandType;
+ }
+
+ /**
+ * Creates a new EditTutTagListCommand.
+ *
+ * @param commandType indicates the command subtype.
+ */
+ public EditTutTagListCommand(CommandSubtype commandType) {
+ this.commandType = commandType;
+ this.tagName = null;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ if (commandType == CommandSubtype.ADD) {
+ TutorialTag tag = new TutorialTag(tagName, TagStatus.AVAILABLE);
+ if (model.hasTutorialTag(tag)) {
+ throw new CommandException(MESSAGE_DUPLICATE_TUTORIALTAG);
+ }
+ model.addTutorialTag(tag);
+ }
+
+ if (commandType == CommandSubtype.DELETE) {
+ // Check if specified tutorial tag exists
+ TutorialTag tag = new TutorialTag(tagName, TagStatus.AVAILABLE);
+ if (!model.hasTutorialTag(tag)) {
+ throw new CommandException(Messages.MESSAGE_INVALID_TUTORIAL_TAG_VALUE + tagName);
+ }
+
+ model.deleteTutorialTag(new TutorialTag(tagName, TagStatus.AVAILABLE));
+
+ // Remove the specified tag from all persons
+ List entireList = model.getPersonList();
+ for (Person person : entireList) {
+ Set currTags = new HashSet<>(person.getTags());
+ assert person != null;
+ Person editedPerson = createEditedPerson(person, Tag.removeTutTagFromTagSet(currTags, this.tagName));
+
+ // Update the person list
+ model.setPerson(person, editedPerson);
+ }
+
+ }
+
+ if (commandType == CommandSubtype.LIST) {
+ // nothing needs to be done
+ }
+
+ return new CommandResult(model.getTutorialTagListString());
+ }
+
+ /**
+ * Creates and returns a {@code Person} with the details of {@code personToEdit}
+ */
+ private static Person createEditedPerson(Person personToEdit, Set newTags) {
+ assert personToEdit != null;
+ return Person.of(personToEdit.getType(), personToEdit.getName(), personToEdit.getId(),
+ personToEdit.getPhone(), personToEdit.getEmail(), newTags);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditTutTagListCommand)) {
+ return false;
+ }
+
+ EditTutTagListCommand otherFindCommand = (EditTutTagListCommand) other;
+ return tagName.equals(otherFindCommand.tagName);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("tagName", tagName)
+ .toString();
+ }
+
+ public static boolean isListCommand(CommandSubtype type) {
+ return type == CommandSubtype.LIST;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index 72b9eddd3a7..7f8e73ac738 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -2,34 +2,43 @@
import static java.util.Objects.requireNonNull;
+import java.util.List;
+
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.person.FieldContainsKeywordsPredicate;
/**
* Finds and lists all persons in address book whose name contains any of the argument keywords.
- * Keyword matching is case insensitive.
+ * Keyword matching is case-insensitive.
*/
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"
- + "Parameters: KEYWORD [MORE_KEYWORDS]...\n"
- + "Example: " + COMMAND_WORD + " alice bob charlie";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters all persons whose contact details contain "
+ + "the specified keywords (case-insensitive) under the specified flag and displays them as a list with "
+ + "index numbers.\n"
+ + "Parameters: [stu | ta] [/n NAME] [/i ID] [/p PHONE] [/e EMAIL]\n"
+ + "Example: " + COMMAND_WORD + " stu /n grace /p 900";
- private final NameContainsKeywordsPredicate predicate;
+ private final List predicates;
- public FindCommand(NameContainsKeywordsPredicate predicate) {
- this.predicate = predicate;
+ public FindCommand(List predicates) {
+ this.predicates = predicates;
}
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.updateFilteredPersonList(predicate);
+ model.persistentUpdateFilteredList(this.predicates);
+
+ int filteredListSize = model.getFilteredPersonList().size();
+ if (filteredListSize <= 1) {
+ return new CommandResult(
+ String.format(Messages.MESSAGE_PERSON_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
+ }
return new CommandResult(
String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
}
@@ -46,13 +55,15 @@ public boolean equals(Object other) {
}
FindCommand otherFindCommand = (FindCommand) other;
- return predicate.equals(otherFindCommand.predicate);
+ return predicates.equals(otherFindCommand.predicates);
}
-
+ // UPDATE THIS
@Override
public String toString() {
- return new ToStringBuilder(this)
- .add("predicate", predicate)
- .toString();
+ ToStringBuilder sb = new ToStringBuilder(this);
+ for (FieldContainsKeywordsPredicate predicate : predicates) {
+ sb.add("predicate", predicate);
+ }
+ return sb.toString();
}
}
diff --git a/src/main/java/seedu/address/logic/commands/MarkCommand.java b/src/main/java/seedu/address/logic/commands/MarkCommand.java
new file mode 100644
index 00000000000..c4bbcf34604
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/MarkCommand.java
@@ -0,0 +1,163 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAGSTATUS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+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.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TagType;
+import seedu.address.model.tag.TutorialTag;
+
+/**
+ * Changes the remark of an existing person in the address book.
+ */
+public class MarkCommand extends Command {
+
+ public static final String COMMAND_WORD = "mark";
+
+ public static final String SAMPLE_COMMAND = COMMAND_WORD + " 1 " + PREFIX_TAG
+ + " assignment1 " + PREFIX_TAGSTATUS + " cg";
+ public static final String SAMPLE_COMMAND_2 = COMMAND_WORD + " 1 2 " + PREFIX_TAG
+ + " assignment2 assignment3 " + PREFIX_TAGSTATUS + " ig";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Updates the status of the specified tag(s) "
+ + "of specified contact entry(s) with the specified status.\n"
+ + "If the tag(s) specified does not exist, a new tag with the tag name"
+ + " and tag status would be created.\n"
+ + "Parameters: (all | INDEX (must be a positive integer within the size of the displayed list) "
+ + "[OTHER_INDICES]) "
+ + PREFIX_TAG + " TAG_NAME [OTHER_TAG_NAMES] " + PREFIX_TAGSTATUS + " TAG_STATUS\n"
+ + "Example 1: " + SAMPLE_COMMAND + "\n"
+ + "Example 2: " + SAMPLE_COMMAND_2;
+
+ public static final String MESSAGE_MARK_PERSON_SUCCESS = "Updated Person: %1$s";
+ private final Set indices;
+ private final Set tagNames;
+ private final TagStatus tagStatus;
+
+ /**
+ * @param indices of the person in the filtered person list to update tag status
+ * @param tagNames name of the tag whose status is to be updated
+ * @param tagStatus the status to update the specified tag with
+ */
+ public MarkCommand(Set indices, Set tagNames, TagStatus tagStatus) {
+ requireAllNonNull(indices, tagNames, tagStatus);
+ this.indices = indices;
+ this.tagNames = tagNames;
+ this.tagStatus = tagStatus;
+ }
+
+ /**
+ * Edit the tags of a single {@code Person}.
+ */
+ private StringBuilder executeHelper(Model model, Index index) throws CommandException {
+ requireAllNonNull(model, index);
+ List lastShownList = model.getFilteredPersonList();
+
+ // check whether index specified is within valid range
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(String.format(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX,
+ index.getOneBased()));
+ }
+
+ Person personToEdit = lastShownList.get(index.getZeroBased());
+ Set currTags = new HashSet<>(personToEdit.getTags());
+
+ // create a new person with the new tag(s), necessary as the person fields are final
+ Person editedPerson = createEditedPerson(personToEdit, Tag.updateTagsWithNewTags(currTags,
+ this.tagNames, this.tagStatus));
+
+ model.setPerson(personToEdit, editedPerson);
+
+ return new StringBuilder()
+ .append(String.format(MESSAGE_MARK_PERSON_SUCCESS, Messages.format(editedPerson)))
+ .append("\n");
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ // need not check for duplicate indices because a Set is used
+
+ //
+
+ // only tutorial tags with predefined tag names are allowed
+ if (Tag.getTagTypeWithTagStatus(tagStatus) == TagType.TUTORIAL) {
+ for (String tagName: tagNames) {
+ TutorialTag tag = new TutorialTag(tagName, TagStatus.AVAILABLE);
+ if (!model.hasTutorialTag(tag)) {
+ throw new CommandException(Messages.MESSAGE_INVALID_TUTORIAL_TAG_VALUE + tagName);
+ }
+ }
+ }
+
+ StringBuilder resultStringBuilder;
+ try {
+ resultStringBuilder = indices.stream().map(x -> {
+ try {
+ return executeHelper(model, x);
+ } catch (CommandException e) {
+ throw new RuntimeException(e);
+ }
+ }).reduce(StringBuilder::append).orElse(new StringBuilder());
+ } catch (RuntimeException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof CommandException) {
+ throw (CommandException) cause;
+ } else {
+ throw e;
+ }
+ }
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ resultStringBuilder.deleteCharAt(resultStringBuilder.length() - 1); // remove the last '\n'
+ return new CommandResult(resultStringBuilder.toString());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof MarkCommand)) {
+ return false;
+ }
+
+ // state check
+ MarkCommand e = (MarkCommand) other;
+ return indices.equals(e.indices)
+ && tagNames.equals(e.tagNames)
+ && tagStatus.equals(e.tagStatus);
+ }
+
+ /**
+ * Creates and returns a {@code Person} with the details of {@code personToEdit}
+ */
+ private static Person createEditedPerson(Person personToEdit, Set newTags) {
+ assert personToEdit != null;
+ return Person.of(personToEdit.getType(), personToEdit.getName(), personToEdit.getId(),
+ personToEdit.getPhone(), personToEdit.getEmail(), newTags);
+ }
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", indices.toString())
+ .add("tagName(s)", tagNames)
+ .add("tagStatus", tagStatus)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/RemovetagCommand.java b/src/main/java/seedu/address/logic/commands/RemovetagCommand.java
new file mode 100644
index 00000000000..0d4407c14d1
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/RemovetagCommand.java
@@ -0,0 +1,133 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+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.address.model.tag.Tag;
+
+/**
+ * Changes the remark of an existing person in the address book.
+ */
+public class RemovetagCommand extends Command {
+
+ public static final String COMMAND_WORD = "removetag";
+ public static final String SAMPLE_COMMAND = COMMAND_WORD + " 1 " + PREFIX_TAG + " assignment1 ";
+ // to be updated
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Removes the specified tag from the specified"
+ + " contact entry.\n"
+ + "Parameters: (all | INDEX (must be a positive integer within the size of the displayed list) "
+ + "[OTHER_INDICES]) "
+ + PREFIX_TAG + " TAG\n"
+ + "Example: " + SAMPLE_COMMAND;
+
+ public static final String MESSAG_REMOVETAG_SUCCESS = "Updated Person: %1$s";
+ private final Set indices;
+ private final Set tagNames;
+
+ /**
+ * @param indices of the person in the filtered person list to have the tag deleted.
+ * @param tagNames name of the tag to be deleted.
+ */
+ public RemovetagCommand(Set indices, Set tagNames) {
+ requireAllNonNull(indices, tagNames);
+ this.indices = indices;
+ this.tagNames = tagNames;
+ }
+
+ /**
+ * Removes the specified tags of a single {@code Person}.
+ */
+ private StringBuilder executeHelper(Model model, Index index) throws CommandException {
+ requireAllNonNull(model, index);
+ List lastShownList = model.getFilteredPersonList();
+
+ // check whether index specified is within valid range
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToEdit = lastShownList.get(index.getZeroBased());
+ Set currTags = new HashSet<>(personToEdit.getTags());
+
+ // create a new person with the new tag(s), necessary as the person fields are final
+ Person editedPerson = createEditedPerson(personToEdit, Tag.removeTagsFromTagSet(currTags,
+ this.tagNames));
+
+ model.setPerson(personToEdit, editedPerson);
+
+ return new StringBuilder()
+ .append(String.format(MESSAG_REMOVETAG_SUCCESS, Messages.format(editedPerson)))
+ .append("\n");
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ // need not check for duplicate indices because a Set is used
+
+ StringBuilder resultStringBuilder;
+ try {
+ resultStringBuilder = indices.stream().map(x -> {
+ try {
+ return executeHelper(model, x);
+ } catch (CommandException e) {
+ throw new RuntimeException(e);
+ }
+ }).reduce(StringBuilder::append).orElse(new StringBuilder());
+ } catch (RuntimeException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof CommandException) {
+ throw (CommandException) cause;
+ } else {
+ throw e;
+ }
+ }
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ resultStringBuilder.deleteCharAt(resultStringBuilder.length() - 1); // remove the last '\n'
+ return new CommandResult(resultStringBuilder.toString());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof RemovetagCommand)) {
+ return false;
+ }
+
+ // state check
+ RemovetagCommand e = (RemovetagCommand) other;
+ return indices.equals(e.indices)
+ && tagNames.equals(e.tagNames);
+ }
+
+ /**
+ * Creates and returns a {@code Person} with the details of {@code personToEdit}
+ */
+ private static Person createEditedPerson(Person personToEdit, Set newTags) {
+ assert personToEdit != null;
+ return Person.of(personToEdit.getType(), personToEdit.getName(), personToEdit.getId(),
+ personToEdit.getPhone(), personToEdit.getEmail(), newTags);
+ }
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", indices.toString())
+ .add("tagName(s)", tagNames).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
index a16bd14f2cd..f44c0c19186 100644
--- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
+++ b/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java
@@ -1,5 +1,7 @@
package seedu.address.logic.commands.exceptions;
+import seedu.address.logic.commands.Command;
+
/**
* Represents an error which occurs during execution of a {@link Command}.
*/
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
index 4ff1a97ed77..bdcc9ef0c5f 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
@@ -1,21 +1,20 @@
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_ID;
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 java.util.HashSet;
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.Id;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
+import seedu.address.model.person.PersonType;
import seedu.address.model.person.Phone;
import seedu.address.model.tag.Tag;
@@ -31,31 +30,24 @@ public class AddCommandParser implements Parser {
*/
public AddCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_ID, PREFIX_PHONE,
+ PREFIX_EMAIL);
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
- || !argMultimap.getPreamble().isEmpty()) {
+ if (!StatefulParserUtil.arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ID, PREFIX_PHONE,
+ PREFIX_EMAIL)) {
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));
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_ID, PREFIX_PHONE, PREFIX_EMAIL);
+ PersonType type = StatefulParserUtil.parsePersonType(argMultimap.getPreamble());
+ Name name = StatefulParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
+ Id id = StatefulParserUtil.parseId(argMultimap.getValue(PREFIX_ID).get());
+ Phone phone = StatefulParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
+ Email email = StatefulParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
- Person person = new Person(name, phone, email, address, tagList);
+ Person person = Person.of(type, name, id, phone, email, new HashSet());
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
index 3149ee07e0b..c94126b574e 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -9,14 +9,18 @@
import seedu.address.commons.core.LogsCenter;
import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AvailableCommand;
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.EditTutTagListCommand;
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.commands.MarkCommand;
+import seedu.address.logic.commands.RemovetagCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -44,7 +48,7 @@ public Command parseCommand(String userInput) throws ParseException {
}
final String commandWord = matcher.group("commandWord");
- final String arguments = matcher.group("arguments");
+ 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.
@@ -65,6 +69,12 @@ public Command parseCommand(String userInput) throws ParseException {
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
+ case MarkCommand.COMMAND_WORD:
+ return new MarkCommandParser().parse(arguments);
+
+ case RemovetagCommand.COMMAND_WORD:
+ return new RemovetagCommandParser().parse(arguments);
+
case FindCommand.COMMAND_WORD:
return new FindCommandParser().parse(arguments);
@@ -77,6 +87,12 @@ public Command parseCommand(String userInput) throws ParseException {
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
+ case EditTutTagListCommand.COMMAND_WORD:
+ return new EditTutTagListCommandParser().parse(arguments);
+
+ case AvailableCommand.COMMAND_WORD:
+ return new AvailableCommandParser().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/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
index 5c9aebfa488..b20e55857bc 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
+++ b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
@@ -70,7 +70,7 @@ private static List findPrefixPositions(String argsString, Prefi
* {@code fromIndex} = 0, this method returns 5.
*/
private static int findPrefixPosition(String argsString, String prefix, int fromIndex) {
- int prefixIndex = argsString.indexOf(" " + prefix, fromIndex);
+ int prefixIndex = argsString.indexOf(" " + prefix + " ", fromIndex);
return prefixIndex == -1 ? -1
: prefixIndex + 1; // +1 as offset for whitespace
}
diff --git a/src/main/java/seedu/address/logic/parser/AvailableCommandParser.java b/src/main/java/seedu/address/logic/parser/AvailableCommandParser.java
new file mode 100644
index 00000000000..12a6d7eabf5
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AvailableCommandParser.java
@@ -0,0 +1,61 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.commons.util.AppUtil.checkArgument;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_TAG_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+
+import java.util.Optional;
+
+import seedu.address.logic.commands.AvailableCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.TutorialTagContainsGroupPredicate;
+import seedu.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TutorialTag;
+
+/**
+ * Parses input arguments and creates a new AvailableCommand object
+ */
+public class AvailableCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AvailableCommand
+ * and returns an AvailableCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AvailableCommand parse(String args) throws ParseException {
+ if (args.trim().isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AvailableCommand.MESSAGE_USAGE));
+ }
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_GROUP);
+
+ String preamble = argMultimap.getPreamble();
+ if (!preamble.equals("")) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AvailableCommand.MESSAGE_USAGE));
+ }
+
+ Optional group = argMultimap.getValue(PREFIX_GROUP);
+
+ try {
+ checkArgument(!group.isEmpty(), String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ AvailableCommand.MESSAGE_USAGE));
+ checkArgument(!group.get().equals(""), AvailableCommand.MESSAGE_NON_EMPTY_GROUP_NAME);
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(e.getMessage());
+ }
+
+ String tutorialGroup = argMultimap.getValue(PREFIX_GROUP).get();
+
+ if (!Tag.isValidTagName(tutorialGroup)) {
+ throw new ParseException(MESSAGE_INVALID_TAG_NAME);
+ }
+
+ TutorialTag tutorialTag = new TutorialTag(tutorialGroup, TagStatus.AVAILABLE);
+
+ return new AvailableCommand(new TutorialTagContainsGroupPredicate(tutorialGroup), tutorialTag);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..89bf56529d1 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -1,15 +1,21 @@
package seedu.address.logic.parser;
+import seedu.address.model.person.PersonType;
+
/**
* 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/");
-
+ public static final PersonType TYPE_STU = PersonType.STU;
+ public static final PersonType TYPE_TA = PersonType.TA;
+ public static final PersonType TYPE_DEFAULT = PersonType.STU;
+ public static final Prefix PREFIX_NAME = new Prefix("/n");
+ public static final Prefix PREFIX_ID = new Prefix("/i");
+ public static final Prefix PREFIX_PHONE = new Prefix("/p");
+ public static final Prefix PREFIX_EMAIL = new Prefix("/e");
+ public static final Prefix PREFIX_TAG = new Prefix("/t");
+ public static final Prefix PREFIX_GROUP = new Prefix("/g");
+ public static final Prefix PREFIX_TAGSTATUS = new Prefix("/ts");
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
index 3527fe76a3e..344f11045b5 100644
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
@@ -2,6 +2,8 @@
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import java.util.Set;
+
import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.parser.exceptions.ParseException;
@@ -17,9 +19,11 @@ public class DeleteCommandParser implements Parser {
* @throws ParseException if the user input does not conform the expected format
*/
public DeleteCommand parse(String args) throws ParseException {
+ Set indices;
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args);
try {
- Index index = ParserUtil.parseIndex(args);
- return new DeleteCommand(index);
+ indices = StatefulParserUtil.parseIndices(argMultimap.getPreamble());
+ return new DeleteCommand(indices);
} catch (ParseException pe) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe);
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
index 46b3309a78b..a6e84e99b9d 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
@@ -2,11 +2,9 @@
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;
@@ -31,34 +29,29 @@ public class EditCommandParser implements Parser {
*/
public EditCommand parse(String args) throws ParseException {
requireNonNull(args);
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL);
Index index;
try {
- index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ index = StatefulParserUtil.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);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL);
EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
- editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
+ editPersonDescriptor.setName(StatefulParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
}
if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
- editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
+ editPersonDescriptor.setPhone(StatefulParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
}
if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
- editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
+ editPersonDescriptor.setEmail(StatefulParserUtil.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);
@@ -79,7 +72,7 @@ private Optional> parseTagsForEdit(Collection tags) throws Pars
return Optional.empty();
}
Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags;
- return Optional.of(ParserUtil.parseTags(tagSet));
+ return Optional.of(StatefulParserUtil.parseTags(tagSet));
}
}
diff --git a/src/main/java/seedu/address/logic/parser/EditTutTagListCommandParser.java b/src/main/java/seedu/address/logic/parser/EditTutTagListCommandParser.java
new file mode 100644
index 00000000000..cb4f7a243bc
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/EditTutTagListCommandParser.java
@@ -0,0 +1,55 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+
+import seedu.address.logic.commands.EditTutTagListCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Parses input arguments and creates a new EditTutTagListCommand object
+ */
+public class EditTutTagListCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the EditTutTagListCommand
+ * and returns a EditTutTagListCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public EditTutTagListCommand parse(String args) throws ParseException {
+ if (args.trim().isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTutTagListCommand.MESSAGE_USAGE));
+ }
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TAG);
+
+ String commandFlag = argMultimap.getPreamble();
+ try {
+ EditTutTagListCommand.CommandSubtype commandSubtype = StatefulParserUtil.isCreatingNewTag(commandFlag);
+
+ // if the EditTutTagListCommand is to list all available tutorial tags
+ if (EditTutTagListCommand.isListCommand(commandSubtype)) {
+ return new EditTutTagListCommand(commandSubtype);
+ }
+
+ // if the EditTutTagListCommand is not to list all available tutorial tags, PREFIX_TAG must be present
+ if (!StatefulParserUtil.arePrefixesPresent(argMultimap, PREFIX_TAG)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ EditTutTagListCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_TAG);
+ String tagName = argMultimap.getValue(PREFIX_TAG).get();
+ Tag.isTagNameValid(tagName);
+ return new EditTutTagListCommand(tagName, commandSubtype);
+
+ } catch (ParseException e) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTutTagListCommand.MESSAGE_USAGE));
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
index 2867bde857b..ec9577f81b3 100644
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
@@ -1,12 +1,22 @@
package seedu.address.logic.parser;
+import static seedu.address.commons.util.AppUtil.checkArgument;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ID;
+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.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import seedu.address.commons.util.StringListUtil;
import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.person.FieldContainsKeywordsPredicate;
+import seedu.address.model.person.PersonType;
/**
* Parses input arguments and creates a new FindCommand object
@@ -19,15 +29,40 @@ public class FindCommandParser implements Parser {
* @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()) {
+ if (args.trim().isEmpty()) {
throw new ParseException(
String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
}
- String[] nameKeywords = trimmedArgs.split("\\s+");
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_ID, PREFIX_PHONE,
+ PREFIX_EMAIL, PREFIX_TAG);
- return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
- }
+ List predicates = new ArrayList<>();
+
+ PersonType type = StatefulParserUtil.parseFindPersonType(argMultimap.getPreamble());
+ if (type != null) {
+ List keyword = new ArrayList<>();
+ keyword.add(type.toString());
+ predicates.add(new FieldContainsKeywordsPredicate(keyword));
+ }
+ List allPrefixes = Arrays.asList(PREFIX_NAME, PREFIX_ID, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_TAG);
+ for (Prefix prefix: allPrefixes) {
+ if (StatefulParserUtil.arePrefixesPresent(argMultimap, prefix)) {
+ List keywords = argMultimap.getAllValues(prefix);
+ List separated = StringListUtil.separateWithSpaces(keywords);
+ try {
+ for (String keyword : separated) {
+ checkArgument(!keyword.isEmpty(), "Word parameter cannot be empty");
+ }
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(e.getMessage());
+ }
+ predicates.add(new FieldContainsKeywordsPredicate(prefix, separated));
+ }
+ }
+
+ return new FindCommand(predicates);
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/MarkCommandParser.java b/src/main/java/seedu/address/logic/parser/MarkCommandParser.java
new file mode 100644
index 00000000000..503e87a17e6
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/MarkCommandParser.java
@@ -0,0 +1,63 @@
+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_TAG;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAGSTATUS;
+
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.logic.commands.MarkCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
+
+
+/**
+ * Parses input arguments and creates a new {@code RemarkCommand} object
+ */
+public class MarkCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the {@code MarkCommand}
+ * and returns a {@code MarkCommand} object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public MarkCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TAG, PREFIX_TAGSTATUS);
+
+ Set indices;
+ try {
+ indices = StatefulParserUtil.parseIndices(argMultimap.getPreamble());
+ } catch (IllegalValueException ive) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkCommand.MESSAGE_USAGE), ive);
+ }
+
+ if (!StatefulParserUtil.arePrefixesPresent(argMultimap, PREFIX_TAG, PREFIX_TAGSTATUS)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_TAG, PREFIX_TAGSTATUS);
+ String tagNamesString = argMultimap.getValue(PREFIX_TAG).get();
+ Set tagNames = StatefulParserUtil.parseTagNamesString(tagNamesString);
+
+ String statusIdentifier = argMultimap.getValue(PREFIX_TAGSTATUS).get();
+
+
+
+ // an alternative approach is to instantiate the Tag object and try to
+ // catch the Illegal Exception Error. The tag can then be fed into the
+ // MarkCommand. The author decided to pass in the tagName instead as the
+ // TagName might be used to search for tags in future implementations
+ try {
+ TagStatus tagStatus = TagStatus.getTagStatus(statusIdentifier);
+ tagNames.forEach(Tag::isTagNameValid);
+ return new MarkCommand(indices, tagNames, tagStatus);
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(e.getMessage());
+ }
+ }
+
+}
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/logic/parser/RemovetagCommandParser.java b/src/main/java/seedu/address/logic/parser/RemovetagCommandParser.java
new file mode 100644
index 00000000000..f9dc8ddabec
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/RemovetagCommandParser.java
@@ -0,0 +1,53 @@
+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_TAG;
+
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.logic.commands.RemovetagCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Parses input arguments and creates a new {@code RemarkCommand} object
+ */
+public class RemovetagCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the {@code RemovetagCommand}
+ * and returns a {@code RemovetagCommand} object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public RemovetagCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TAG);
+
+ Set indices;
+ try {
+ indices = StatefulParserUtil.parseIndices(argMultimap.getPreamble());
+ } catch (IllegalValueException ive) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ RemovetagCommand.MESSAGE_USAGE), ive);
+ }
+
+ if (!StatefulParserUtil.arePrefixesPresent(argMultimap, PREFIX_TAG)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ RemovetagCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_TAG);
+ String tagNamesString = argMultimap.getValue(PREFIX_TAG).get();
+ Set tagNames = StatefulParserUtil.parseTagNamesString(tagNamesString);
+
+ try {
+ tagNames.forEach(Tag::isTagNameValid);
+ return new RemovetagCommand(indices, tagNames);
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(e.getMessage());
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/StatefulParserUtil.java b/src/main/java/seedu/address/logic/parser/StatefulParserUtil.java
new file mode 100644
index 00000000000..474360f92bb
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/StatefulParserUtil.java
@@ -0,0 +1,285 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.StatefulStringUtil;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.EditTutTagListCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Id;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.PersonType;
+import seedu.address.model.person.Phone;
+import seedu.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
+
+/**
+ * Contains utility methods used for parsing strings in the various *Parser classes.
+ */
+public class StatefulParserUtil {
+ public static final String MESSAGE_INVALID_INDEX = "Index is out of bounds or not an unsigned positive integer.";
+ private static StatefulParserUtil instance = null;
+ private Model model;
+ private StatefulParserUtil(Model model) {
+ this.model = model;
+ }
+
+ /**
+ * Initializes the {@code StatefulParserUtil} singleton with a model.
+ */
+ public static void initialize(Model model) {
+ if (instance == null) {
+ instance = new StatefulParserUtil(model);
+ }
+ }
+
+ /**
+ * Get the instance of the {@code StatefulParserUtil} singleton.
+ */
+ public static StatefulParserUtil getInstance() {
+ if (instance == null) {
+ throw new IllegalStateException("Instance not initialized yet.");
+ }
+ return instance;
+ }
+
+ /**
+ * 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 (!StatefulStringUtil.isNonZeroUnsignedInteger(trimmedIndex)) {
+ throw new ParseException(MESSAGE_INVALID_INDEX);
+ }
+
+ int intIndex = Integer.parseInt(trimmedIndex);
+ Model model = StatefulParserUtil.getInstance().model;
+ if (intIndex > model.getFilteredPersonList().size()) {
+ throw new ParseException(MESSAGE_INVALID_INDEX);
+ }
+ return Index.fromOneBased(intIndex);
+ }
+
+ /**
+ * Parses a {@code String} consisting of indices into a {@code Set}.
+ *
+ * @throws ParseException if any substring is not a valid index.
+ */
+ public static Set parseIndices(String oneBasedIndices) throws ParseException {
+ requireNonNull(oneBasedIndices);
+ String trimmedOneBasedIndices = oneBasedIndices.trim();
+
+ Set parsedIndices = new HashSet<>();
+ List indicesList;
+ if (trimmedOneBasedIndices.equals("all")) {
+ Model model = StatefulParserUtil.getInstance().model;
+ int size = model.getFilteredPersonList().size();
+ indicesList = Stream.iterate(1, x -> x + 1)
+ .limit(size)
+ .map(Objects::toString)
+ .collect(Collectors.toList());
+ } else {
+ indicesList = Arrays.asList(trimmedOneBasedIndices.split("\\s+"));
+ }
+
+ // Java Lambda expressions do not allow propagating of checked exceptions.
+ // To circumvent this, wrap the checked exception in an unchecked exception.
+ try {
+ parsedIndices = indicesList.stream().map(x -> {
+ try {
+ return parseIndex(x);
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }).collect(Collectors.toSet());
+ } catch (RuntimeException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof ParseException) {
+ throw (ParseException) cause;
+ } else {
+ throw e;
+ }
+ }
+
+ return parsedIndices;
+ }
+
+ /**
+ * Parses a {@code String type} into a {@code PersonType}.
+ * Leading and trailing whitespaces will be trimmed.
+ * An unspecified person type will default to student.
+ *
+ * @throws ParseException if the given {@code type} is invalid.
+ */
+ public static PersonType parsePersonType(String type) throws ParseException {
+ requireNonNull(type);
+ String trimmedType = type.trim();
+ if (!PersonType.isValidPersonType(trimmedType) && !trimmedType.isEmpty()) {
+ throw new ParseException(PersonType.MESSAGE_CONSTRAINTS);
+ }
+ return PersonType.getPersonType(trimmedType);
+ }
+
+ /**
+ * Parses a {@code String type} into a {@code PersonType} for the find command.
+ * Leading and trailing whitespaces will be trimmed.
+ * An unspecified person type will return null.
+ *
+ * @throws ParseException if the given {@code type} is invalid.
+ */
+ public static PersonType parseFindPersonType(String type) throws ParseException {
+ requireNonNull(type);
+ String trimmedType = type.trim();
+ if (!PersonType.isValidPersonType(trimmedType) && !trimmedType.isEmpty()) {
+ throw new ParseException(PersonType.MESSAGE_CONSTRAINTS);
+ } else if (trimmedType.isEmpty()) {
+ return null;
+ } else {
+ return PersonType.getPersonType(trimmedType);
+ }
+ }
+
+
+ /**
+ * 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 id} into a {@code Id}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code id} is invalid.
+ */
+ public static Id parseId(String id) throws ParseException {
+ requireNonNull(id);
+ String trimmedId = id.trim();
+ if (!Id.isValidId(trimmedId)) {
+ throw new ParseException(Id.MESSAGE_CONSTRAINTS);
+ }
+ return new Id(trimmedId);
+ }
+
+ /**
+ * 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 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 Tag.createTag(trimmedTag, TagStatus.DEFAULT_STATUS);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Parses a {@code String} of tag names separated by whitespaces
+ * into a {@code Set}, where each element corresponds to a
+ * trimmed tag name.
+ */
+ public static Set parseTagNamesString(String tagNames) {
+ requireNonNull(tagNames);
+ final Set tagNamesSet = new HashSet<>();
+ tagNamesSet.addAll(Arrays.asList(tagNames.split("\\s+")));
+ return tagNamesSet;
+ }
+
+ /**
+ * Returns true if none of the prefixes contains empty {@code Optional} values in the given
+ * {@code ArgumentMultimap}.
+ */
+ public static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
+ /**
+ * @param flag Command flag on whether the EditTutTagListCommand
+ * @throws ParseException
+ */
+ public static EditTutTagListCommand.CommandSubtype isCreatingNewTag(String flag) throws ParseException {
+ if (flag.equals(EditTutTagListCommand.ADD_FLAG)) {
+ return EditTutTagListCommand.CommandSubtype.ADD;
+ }
+
+ if (flag.equals(EditTutTagListCommand.DELETE_FLAG)) {
+ return EditTutTagListCommand.CommandSubtype.DELETE;
+ }
+
+ if (flag.equals(EditTutTagListCommand.LIST_FLAG)) {
+ return EditTutTagListCommand.CommandSubtype.LIST;
+ }
+
+ throw new ParseException(Messages.MESSAGE_INVALID_COMMAND_FORMAT);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 73397161e84..7bcc1b83894 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -8,6 +8,8 @@
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.model.person.Person;
import seedu.address.model.person.UniquePersonList;
+import seedu.address.model.person.UniqueTutorialTagList;
+import seedu.address.model.tag.TutorialTag;
/**
* Wraps all data at the address-book level
@@ -16,6 +18,7 @@
public class AddressBook implements ReadOnlyAddressBook {
private final UniquePersonList persons;
+ private final UniqueTutorialTagList tutorialTags;
/*
* The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
@@ -26,6 +29,7 @@ public class AddressBook implements ReadOnlyAddressBook {
*/
{
persons = new UniquePersonList();
+ tutorialTags = new UniqueTutorialTagList();
}
public AddressBook() {}
@@ -48,6 +52,14 @@ public void setPersons(List persons) {
this.persons.setPersons(persons);
}
+ /**
+ * Replaces the contents of the tutorial tags list with {@code tutorialTags}.
+ * {@code tutorialTags} must not contain duplicate tutorial tags.
+ */
+ public void setTutorialTags(List tutorialTags) {
+ this.tutorialTags.setTutorialTags(tutorialTags);
+ }
+
/**
* Resets the existing data of this {@code AddressBook} with {@code newData}.
*/
@@ -55,6 +67,7 @@ public void resetData(ReadOnlyAddressBook newData) {
requireNonNull(newData);
setPersons(newData.getPersonList());
+ setTutorialTags(newData.getTutorialTagList());
}
//// person-level operations
@@ -94,6 +107,31 @@ public void removePerson(Person key) {
persons.remove(key);
}
+ //// tutorialTag-level operations
+
+ /**
+ */
+ public boolean hasTutorialTag(TutorialTag tutorialTag) {
+ requireNonNull(tutorialTag);
+ return tutorialTags.contains(tutorialTag);
+ }
+
+ /**
+ * Adds a tutorialTag to the address book.
+ * The tutorialTag must not already exist in the address book.
+ */
+ public void addTutorialTag(TutorialTag t) {
+ tutorialTags.add(t);
+ }
+
+ /**
+ * Removes {@code key} from this {@code AddressBook}.
+ * {@code key} must exist in the address book.
+ */
+ public void removeTutorialTag(TutorialTag key) {
+ tutorialTags.remove(key);
+ }
+
//// util methods
@Override
@@ -108,6 +146,11 @@ public ObservableList getPersonList() {
return persons.asUnmodifiableObservableList();
}
+ @Override
+ public ObservableList getTutorialTagList() {
+ return tutorialTags.asUnmodifiableObservableList();
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -120,11 +163,27 @@ public boolean equals(Object other) {
}
AddressBook otherAddressBook = (AddressBook) other;
- return persons.equals(otherAddressBook.persons);
+ return persons.equals(otherAddressBook.persons) && tutorialTags.equals(otherAddressBook.tutorialTags);
}
@Override
public int hashCode() {
return persons.hashCode();
}
+
+ public String getTutorialTagListString() {
+ StringBuilder sb = new StringBuilder("Available Tutorial Tag(s): [");
+
+ if (!tutorialTags.isEmpty()) {
+ for (TutorialTag tag : tutorialTags) {
+ sb.append(tag.getTagName()).append(", ");
+ }
+ sb.delete(sb.length() - 2, sb.length()); // Remove the last comma and space
+ } else {
+ return sb.append(" ]").toString();
+ }
+
+ sb.append("]");
+ return sb.toString();
+ }
}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..bafa3a05277 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -1,11 +1,13 @@
package seedu.address.model;
import java.nio.file.Path;
+import java.util.List;
import java.util.function.Predicate;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.model.person.Person;
+import seedu.address.model.tag.TutorialTag;
/**
* The API of the Model component.
@@ -76,12 +78,43 @@ public interface Model {
*/
void setPerson(Person target, Person editedPerson);
+ /** Returns an unmodifiable view of the entire person list */
+ ObservableList getPersonList();
+
/** Returns an unmodifiable view of the filtered person list */
ObservableList getFilteredPersonList();
+ /**
+ * Applies multiple predicates to filter the filtered person list.
+ * @param predicates List of predicates.
+ */
+ void persistentUpdateFilteredList(List extends Predicate> predicates);
/**
* 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);
+
+ /**
+ * Deletes the given tutorial tag.
+ * The tutorial tag must exist in the address book.
+ */
+ void deleteTutorialTag(TutorialTag target);
+
+ /**
+ * Adds the given tutorial tag.
+ * {@code tutorialTag} must not already exist in the address book.
+ */
+ void addTutorialTag(TutorialTag tutorialTag);
+
+ /**
+ * Returns true if a tutorial tag with the same identity as {@code tutorialTag} exists in the address book.
+ */
+ boolean hasTutorialTag(TutorialTag tutorialTag);
+
+ /** Returns an unmodifiable view of the filtered tutorial tag list */
+ ObservableList getTutorialTagList();
+
+ /** Returns a String representing the filtered tutorial tag list */
+ String getTutorialTagListString();
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..b1c23c62a2e 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -4,6 +4,7 @@
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
import java.nio.file.Path;
+import java.util.List;
import java.util.function.Predicate;
import java.util.logging.Logger;
@@ -12,6 +13,7 @@
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
import seedu.address.model.person.Person;
+import seedu.address.model.tag.TutorialTag;
/**
* Represents the in-memory model of the address book data.
@@ -21,7 +23,7 @@ public class ModelManager implements Model {
private final AddressBook addressBook;
private final UserPrefs userPrefs;
- private final FilteredList filteredPersons;
+ private FilteredList filteredPersons;
/**
* Initializes a ModelManager with the given addressBook and userPrefs.
@@ -30,7 +32,6 @@ 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());
@@ -113,6 +114,11 @@ public void setPerson(Person target, Person editedPerson) {
//=========== Filtered Person List Accessors =============================================================
+ @Override
+ public ObservableList getPersonList() {
+ return addressBook.getPersonList();
+ }
+
/**
* Returns an unmodifiable view of the list of {@code Person} backed by the internal list of
* {@code versionedAddressBook}
@@ -128,6 +134,14 @@ public void updateFilteredPersonList(Predicate predicate) {
filteredPersons.setPredicate(predicate);
}
+ @Override
+ public void persistentUpdateFilteredList(List extends Predicate> predicates) {
+ Predicate combinedPredicate = predicates.stream()
+ .>map(p -> (Predicate) p)
+ .reduce(Predicate::and)
+ .orElse(person -> true);
+ filteredPersons.setPredicate(combinedPredicate);
+ }
@Override
public boolean equals(Object other) {
if (other == this) {
@@ -145,4 +159,28 @@ public boolean equals(Object other) {
&& filteredPersons.equals(otherModelManager.filteredPersons);
}
+ @Override
+ public void deleteTutorialTag(TutorialTag tutorialTag) {
+ addressBook.removeTutorialTag(tutorialTag);
+ }
+
+ @Override
+ public void addTutorialTag(TutorialTag tutorialTag) {
+ addressBook.addTutorialTag(tutorialTag);
+ }
+
+ @Override
+ public boolean hasTutorialTag(TutorialTag tutorialTag) {
+ requireNonNull(tutorialTag);
+ return addressBook.hasTutorialTag(tutorialTag);
+ }
+
+ @Override
+ public ObservableList getTutorialTagList() {
+ return addressBook.getTutorialTagList();
+ }
+
+ public String getTutorialTagListString() {
+ return addressBook.getTutorialTagListString();
+ }
}
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
index 6ddc2cd9a29..f728c7c8eec 100644
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
@@ -2,6 +2,7 @@
import javafx.collections.ObservableList;
import seedu.address.model.person.Person;
+import seedu.address.model.tag.TutorialTag;
/**
* Unmodifiable view of an address book
@@ -14,4 +15,10 @@ public interface ReadOnlyAddressBook {
*/
ObservableList getPersonList();
+ /**
+ * Returns an unmodifiable view of the tutorial tag list.
+ * This list will not contain any duplicate tutorial tags.
+ */
+ ObservableList getTutorialTagList();
+
}
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
index c62e512bc29..2806032f1f1 100644
--- a/src/main/java/seedu/address/model/person/Email.java
+++ b/src/main/java/seedu/address/model/person/Email.java
@@ -14,7 +14,7 @@ public class Email {
+ "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"
+ + "characters and no consecutive special characters are allowed.\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"
diff --git a/src/main/java/seedu/address/model/person/FieldContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/FieldContainsKeywordsPredicate.java
new file mode 100644
index 00000000000..437f4154aaa
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/FieldContainsKeywordsPredicate.java
@@ -0,0 +1,91 @@
+package seedu.address.model.person;
+
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ID;
+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.List;
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StatefulStringUtil;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.parser.Prefix;
+
+/**
+ * Tests that a {@code Person}'s {@code Name} matches any of the keywords given.
+ */
+public class FieldContainsKeywordsPredicate implements Predicate {
+ private Prefix prefix;
+ private final List keywords;
+
+ /**
+ * Constructor for predicate with a prefix
+ * @param prefix prefix category to search within
+ * @param keywords keywords to search for
+ */
+ public FieldContainsKeywordsPredicate(Prefix prefix, List keywords) {
+ this.prefix = prefix;
+ this.keywords = keywords;
+ }
+
+ /**
+ * Constructor for predicate without a prefix. Used for type finding.
+ * @param keywords keywords to search for
+ */
+ public FieldContainsKeywordsPredicate(List keywords) {
+ this.keywords = keywords;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ if (this.prefix == null) { // type
+ return keywords.stream()
+ .anyMatch(keyword -> StatefulStringUtil
+ .containsSubwordIgnoreCase(person.getType().name(), keyword));
+ } else if (this.prefix.equals(PREFIX_NAME)) {
+ return keywords.stream()
+ .anyMatch(keyword -> StatefulStringUtil
+ .containsSubwordIgnoreCase(person.getName().fullName, keyword));
+ } else if (this.prefix.equals(PREFIX_ID)) {
+ return keywords.stream()
+ .anyMatch(keyword -> StatefulStringUtil
+ .containsSubwordIgnoreCase(person.getId().value, keyword));
+ } else if (this.prefix.equals(PREFIX_PHONE)) {
+ return keywords.stream()
+ .anyMatch(keyword -> StatefulStringUtil
+ .containsSubwordIgnoreCase(person.getPhone().value, keyword));
+ } else if (this.prefix.equals(PREFIX_EMAIL)) {
+ return keywords.stream()
+ .anyMatch(keyword -> StatefulStringUtil
+ .containsSubwordIgnoreCase(person.getEmail().value, keyword));
+ } else if (this.prefix.equals(PREFIX_TAG)) {
+ return keywords.stream()
+ .anyMatch(keyword -> person.getTags().stream()
+ .anyMatch(tag -> StatefulStringUtil.tagContainsWordIgnoreCase(tag, keyword)));
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FieldContainsKeywordsPredicate)) {
+ return false;
+ }
+
+ FieldContainsKeywordsPredicate otherFieldContainsKeywordsPredicate = (FieldContainsKeywordsPredicate) other;
+ return keywords.equals(otherFieldContainsKeywordsPredicate.keywords);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add(this.prefix + " keywords", keywords).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Id.java b/src/main/java/seedu/address/model/person/Id.java
new file mode 100644
index 00000000000..8c1f580a58e
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Id.java
@@ -0,0 +1,60 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Person's student/staff ID in the address book.
+ * Guarantees: immutable; is valid as declared in {@link #isValidId(String)}
+ */
+public class Id {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "ID should start and end with a letter, with 7 digits in between";
+ public static final String VALIDATION_REGEX = "[a-zA-Z][0-9]{7}[a-zA-Z]";
+ public final String value;
+
+ /**
+ * Constructs a {@code Id}.
+ *
+ * @param id A valid id.
+ */
+ public Id(String id) {
+ requireNonNull(id);
+ checkArgument(isValidId(id), MESSAGE_CONSTRAINTS);
+ value = id;
+ }
+
+ /**
+ * Returns true if a given string is a valid id.
+ */
+ public static boolean isValidId(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 Id)) {
+ return false;
+ }
+
+ Id otherId = (Id) other;
+ return value.equals(otherId.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.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
index abe8c46b535..b893c481d84 100644
--- a/src/main/java/seedu/address/model/person/Person.java
+++ b/src/main/java/seedu/address/model/person/Person.java
@@ -2,67 +2,79 @@
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 {
+public abstract 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<>();
-
+ final PersonType type;
+ final Name name;
+ final Id id;
+ final Phone phone;
+ final Email email;
/**
* 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);
+ Person(PersonType type, Name name, Id id, Phone phone, Email email) {
+ requireAllNonNull(type, name, id, phone, email);
+ this.type = type;
this.name = name;
+ this.id = id;
this.phone = phone;
this.email = email;
- this.address = address;
- this.tags.addAll(tags);
}
+ /**
+ * Creates either a Student or a TA.
+ * All fields are compulsory.
+ *
+ * @param type
+ * @param name
+ * @param id
+ * @param phone
+ * @param email
+ * @param tags
+ */
+ public static Person of(PersonType type, Name name, Id id, Phone phone, Email email,
+ Set tags) {
+ requireAllNonNull(type, name, id, phone, email, tags);
+ if (type == PersonType.STU) {
+ return new Student(name, id, phone, email, tags);
+ } else {
+ return new Ta(name, id, phone, email, tags);
+ }
+ }
+
+ public PersonType getType() {
+ return type;
+ }
public Name getName() {
return name;
}
-
+ public Id getId() {
+ return id;
+ }
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);
- }
+ public abstract Set getTags();
/**
- * Returns true if both persons have the same name.
+ * Returns true if both persons have the same id.
* This defines a weaker notion of equality between two persons.
*/
public boolean isSamePerson(Person otherPerson) {
@@ -71,47 +83,6 @@ public boolean isSamePerson(Person otherPerson) {
}
return otherPerson != null
- && otherPerson.getName().equals(getName());
+ && otherPerson.getId().equals(getId());
}
-
- /**
- * 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/PersonType.java b/src/main/java/seedu/address/model/person/PersonType.java
new file mode 100644
index 00000000000..006307c8ad8
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/PersonType.java
@@ -0,0 +1,38 @@
+package seedu.address.model.person;
+
+/**
+ * Represents the possible types of a contact entry in the tracker.
+ * Guarantees: is valid as declared in {@link #isValidPersonType(String)}
+ */
+public enum PersonType {
+ STU,
+ TA;
+
+ public static final String MESSAGE_CONSTRAINTS = "Person type should only be 'stu' or 'ta'";
+
+ /**
+ * Returns the PersonType enum based on the given string.
+ */
+ public static PersonType getPersonType(String type) {
+ switch (type) {
+ case "stu":
+ return PersonType.STU;
+ case "ta":
+ return PersonType.TA;
+ default:
+ return PersonType.STU;
+ }
+ }
+
+ /**
+ * Returns true if a given string is a valid person type.
+ */
+ public static boolean isValidPersonType(String type) {
+ return type.equals("stu") || type.equals("ta");
+ }
+
+ @Override
+ public String toString() {
+ return name().toLowerCase();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Student.java b/src/main/java/seedu/address/model/person/Student.java
new file mode 100644
index 00000000000..4a2a3b91752
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Student.java
@@ -0,0 +1,77 @@
+package seedu.address.model.person;
+
+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 Student in TrAcker.
+ * A Student is a type of Person.
+ */
+public class Student extends Person {
+ private final Set tags = new HashSet<>();
+ /**
+ * Every field must be present and not null.
+ *
+ * @param name
+ * @param id
+ * @param phone
+ * @param email
+ * @param tags
+ */
+ public Student(Name name, Id id, Phone phone, Email email, Set tags) {
+ super(PersonType.STU, name, id, phone, email);
+ this.tags.addAll(tags);
+ }
+
+ @Override
+ public Set getTags() {
+ return Collections.unmodifiableSet(tags);
+ }
+
+ /**
+ * 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 Student)) {
+ return false;
+ }
+
+ Student otherStudent = (Student) other;
+ return name.equals(otherStudent.name)
+ && type.equals(otherStudent.type)
+ && id.equals(otherStudent.id)
+ && phone.equals(otherStudent.phone)
+ && email.equals(otherStudent.email)
+ && tags.equals(otherStudent.tags);
+ }
+
+ @Override
+ public int hashCode() {
+ // use this method for custom fields hashing instead of implementing your own
+ return Objects.hash(type, name, phone, email, tags);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("type", type)
+ .add("name", name)
+ .add("id", id)
+ .add("phone", phone)
+ .add("email", email)
+ .add("tags", tags)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Ta.java b/src/main/java/seedu/address/model/person/Ta.java
new file mode 100644
index 00000000000..fc50343a00f
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Ta.java
@@ -0,0 +1,77 @@
+package seedu.address.model.person;
+
+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 TA in TrAcker.
+ * A TA is a type of Person.
+ */
+public class Ta extends Person {
+ private final Set tags = new HashSet<>();
+ /**
+ * Every field must be present and not null.
+ *
+ * @param name
+ * @param id
+ * @param phone
+ * @param email
+ * @param tags
+ */
+ public Ta(Name name, Id id, Phone phone, Email email, Set tags) {
+ super(PersonType.TA, name, id, phone, email);
+ this.tags.addAll(tags);
+ }
+
+ @Override
+ public Set getTags() {
+ return Collections.unmodifiableSet(tags);
+ }
+
+ /**
+ * 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 Ta)) {
+ return false;
+ }
+
+ Ta otherTA = (Ta) other;
+ return name.equals(otherTA.name)
+ && type.equals(otherTA.type)
+ && id.equals(otherTA.id)
+ && phone.equals(otherTA.phone)
+ && email.equals(otherTA.email)
+ && tags.equals(otherTA.tags);
+ }
+
+ @Override
+ public int hashCode() {
+ // use this method for custom fields hashing instead of implementing your own
+ return Objects.hash(type, name, phone, email, tags);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("type", type)
+ .add("name", name)
+ .add("id", id)
+ .add("phone", phone)
+ .add("email", email)
+ .add("tags", tags)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/TutorialTagContainsGroupPredicate.java b/src/main/java/seedu/address/model/person/TutorialTagContainsGroupPredicate.java
new file mode 100644
index 00000000000..040030e9039
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/TutorialTagContainsGroupPredicate.java
@@ -0,0 +1,48 @@
+package seedu.address.model.person;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StatefulStringUtil;
+import seedu.address.commons.util.ToStringBuilder;
+
+
+/**
+ * Tests that a {@code TA}'s {@code Tag} matches any of the tutorial groups given.
+ */
+public class TutorialTagContainsGroupPredicate implements Predicate {
+ private final String tutorialGroup;
+
+ public TutorialTagContainsGroupPredicate(String tutorialGroup) {
+ this.tutorialGroup = tutorialGroup;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ if (person.getType() != PersonType.TA) {
+ return false;
+ }
+ return person.getTags().stream()
+ .anyMatch(tag -> StatefulStringUtil.containsTutorialGroup(tag, tutorialGroup));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof TutorialTagContainsGroupPredicate)) {
+ return false;
+ }
+
+ TutorialTagContainsGroupPredicate otherNameContainsKeywordsPredicate =
+ (TutorialTagContainsGroupPredicate) other;
+ return tutorialGroup.equals(otherNameContainsKeywordsPredicate.tutorialGroup);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("tutorialGroup", tutorialGroup).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/UniqueTutorialTagList.java b/src/main/java/seedu/address/model/person/UniqueTutorialTagList.java
new file mode 100644
index 00000000000..997cdaaeedd
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/UniqueTutorialTagList.java
@@ -0,0 +1,133 @@
+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.DuplicateTutorialTagException;
+import seedu.address.model.person.exceptions.TutorialTagNotFoundException;
+import seedu.address.model.tag.TutorialTag;
+
+
+/**
+ * A list of tutorial tags that enforces uniqueness between its elements and does not allow nulls.
+ * A tutorial tag is considered unique by comparing using {@code TutorialTag#isSameTutorialTag(TutorialTag)}.
+ * As such, adding and updating of tutorial tags uses TutorialTag#isSameTutorialTag(TutorialTag) for equality so as to
+ * ensure that the tutorial tag being added or updated is unique in terms of identity in the UniqueTutorialTagList.
+ * However, the removal of a tutorial tag uses TutorialTag#equals(Object) so as to ensure that the tutorial tag
+ * with exactly the same TagName will be removed.
+ *
+ * Supports a minimal set of list operations.
+ */
+public class UniqueTutorialTagList implements Iterable {
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent tutorial tag as the given argument.
+ */
+ public boolean contains(TutorialTag toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameTutorialTag);
+ }
+
+ /**
+ * Adds a tutorial tag to the list.
+ * The tutorial tag must not already exist in the list.
+ */
+ public void add(TutorialTag toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateTutorialTagException();
+ }
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Removes the equivalent tutorial tag from the list.
+ * The tutorial tag must exist in the list.
+ */
+ public void remove(TutorialTag toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new TutorialTagNotFoundException();
+ }
+ }
+
+ public void setTutorialTags(UniqueTutorialTagList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code tutorialTags}.
+ * {@code tutorialTags} must not contain duplicate tutorial tags.
+ */
+ public void setTutorialTags(List tutorialTags) {
+ requireAllNonNull(tutorialTags);
+ if (!tutorialTagsAreUnique(tutorialTags)) {
+ throw new DuplicateTutorialTagException();
+ }
+
+ internalList.setAll(tutorialTags);
+ }
+ /**
+ * 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 UniqueTutorialTagList)) {
+ return false;
+ }
+
+ UniqueTutorialTagList otherUniqueTutorialTagsList = (UniqueTutorialTagList) other;
+ return internalList.equals(otherUniqueTutorialTagsList.internalList);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return internalList.toString();
+ }
+
+ public boolean isEmpty() {
+ return internalList.isEmpty();
+ }
+
+ /**
+ * Returns true if {@code tutorialTags} contains only unique tutorial tags.
+ */
+ private boolean tutorialTagsAreUnique(List tutorialTags) {
+ for (int i = 0; i < tutorialTags.size() - 1; i++) {
+ for (int j = i + 1; j < tutorialTags.size(); j++) {
+ if (tutorialTags.get(i).isSameTutorialTag(tutorialTags.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicateTutorialTagException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicateTutorialTagException.java
new file mode 100644
index 00000000000..5ef7b8145da
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/exceptions/DuplicateTutorialTagException.java
@@ -0,0 +1,11 @@
+package seedu.address.model.person.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate TutoiralTags (TutorialTags are considered duplicates
+ * if they have the same TagName).
+ */
+public class DuplicateTutorialTagException extends RuntimeException {
+ public DuplicateTutorialTagException() {
+ super("Operation would result in duplicate TutorialTags");
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/exceptions/TutorialTagNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/TutorialTagNotFoundException.java
new file mode 100644
index 00000000000..d45d7afcb7c
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/exceptions/TutorialTagNotFoundException.java
@@ -0,0 +1,6 @@
+package seedu.address.model.person.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified TutorialTag.
+ */
+public class TutorialTagNotFoundException extends RuntimeException {}
diff --git a/src/main/java/seedu/address/model/tag/AssignmentTag.java b/src/main/java/seedu/address/model/tag/AssignmentTag.java
new file mode 100644
index 00000000000..bb341100bd7
--- /dev/null
+++ b/src/main/java/seedu/address/model/tag/AssignmentTag.java
@@ -0,0 +1,16 @@
+package seedu.address.model.tag;
+
+/**
+ * Represents an assignment tag.
+ */
+public class AssignmentTag extends Tag {
+ /**
+ * Constructs a {@code Tag}.
+ *
+ * @param tagName A valid tag name.
+ * @param tagStatus A valid tag status.
+ */
+ public AssignmentTag(String tagName, TagStatus tagStatus) {
+ super(tagName, tagStatus);
+ }
+}
diff --git a/src/main/java/seedu/address/model/tag/AttendanceTag.java b/src/main/java/seedu/address/model/tag/AttendanceTag.java
new file mode 100644
index 00000000000..7135747a2cd
--- /dev/null
+++ b/src/main/java/seedu/address/model/tag/AttendanceTag.java
@@ -0,0 +1,16 @@
+package seedu.address.model.tag;
+
+/**
+ * Represents an attendance tag.
+ */
+public class AttendanceTag extends Tag {
+ /**
+ * Constructs a {@code Tag}.
+ *
+ * @param tagName A valid tag name.
+ * @param tagStatus A valid tag status.
+ */
+ public AttendanceTag(String tagName, TagStatus tagStatus) {
+ super(tagName, tagStatus);
+ }
+}
diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java
index f1a0d4e233b..6fbb996d368 100644
--- a/src/main/java/seedu/address/model/tag/Tag.java
+++ b/src/main/java/seedu/address/model/tag/Tag.java
@@ -3,26 +3,96 @@
import static java.util.Objects.requireNonNull;
import static seedu.address.commons.util.AppUtil.checkArgument;
+import java.util.Set;
+import java.util.stream.Collectors;
+
/**
* Represents a Tag in the address book.
* Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)}
*/
-public class Tag {
+public abstract 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;
+ private final String tagName;
+ private TagStatus tagStatus;
+ private TagType tagType;
/**
* Constructs a {@code Tag}.
*
* @param tagName A valid tag name.
*/
- public Tag(String tagName) {
+ protected Tag(String tagName, TagStatus tagStatus) {
requireNonNull(tagName);
+ // require the tagStatus not to be null for now
+ // in the future, a null tagStatus input should be set to INCOMPLETE_GOOD
+ // by default
+ requireNonNull(tagStatus);
checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS);
this.tagName = tagName;
+ this.tagStatus = tagStatus;
+ this.tagType = getTagTypeWithTagStatus(tagStatus);
+ }
+
+ /**
+ * Creates a new Tag with specified tagName and tagStatus.
+ *
+ * @param tagName Name of the tag to be created.
+ * @param tagStatus Status of the tag.
+ * @return A new tag of specific type corresponding to the TagStatus input.
+ */
+ public static Tag createTag(String tagName, TagStatus tagStatus) {
+ requireNonNull(tagName);
+ // require the tagStatus not to be null for now
+ // in the future, a null tagStatus input should be set to INCOMPLETE_GOOD
+ // by default
+ requireNonNull(tagStatus);
+ checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS);
+ TagType tagType = getTagTypeWithTagStatus(tagStatus);
+
+ switch (tagType) {
+ case ASSIGNMENT:
+ return new AssignmentTag(tagName, tagStatus);
+ case TUTORIAL:
+ return new TutorialTag(tagName, tagStatus);
+ case ATTENDANCE:
+ return new AttendanceTag(tagName, tagStatus);
+ default:
+ return null;
+ }
+ }
+
+ public String getTagName() {
+ return tagName;
+ }
+
+ public TagStatus getTagStatus() {
+ return tagStatus;
+ }
+
+ public TagType getTagType() {
+ return tagType;
+ }
+
+ public static TagType getTagTypeWithTagStatus(TagStatus ts) {
+ switch (ts) {
+ case COMPLETE_GOOD:
+ case COMPLETE_BAD:
+ case INCOMPLETE_GOOD:
+ case INCOMPLETE_BAD:
+ return TagType.ASSIGNMENT;
+ case PRESENT:
+ case ABSENT:
+ case ABSENT_WITH_REASON:
+ return TagType.ATTENDANCE;
+ case ASSIGNED:
+ case AVAILABLE:
+ return TagType.TUTORIAL;
+ default:
+ return TagType.DEFAULT_TYPE;
+ }
}
/**
@@ -56,7 +126,93 @@ public int hashCode() {
* Format state as text for viewing.
*/
public String toString() {
- return '[' + tagName + ']';
+ return "[ " + tagName + " : " + tagStatus + " ]";
}
+ public static void isTagNameValid(String tagName) throws IllegalArgumentException {
+ checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS);
+ }
+
+ /**
+ * @param currTags current tag set to be updated.
+ * @param tagName name of the new tag.
+ * @param tagStatus tagStatus of the new tag.
+ * @return
+ */
+ public static Set updateTagsWithNewTag(Set currTags, String tagName, TagStatus tagStatus) {
+ // Instead of retrieving the Tag sharing the same name and update it,
+ // remove the potentially existing Tag of the same name from the hashset
+ // and then add in a new Tag with the same tagName but updated tagStatus.
+ // This is because Java Set does not provide a get() method.
+ Tag newTag = Tag.createTag(tagName, tagStatus);
+ currTags.remove(newTag);
+ currTags.add(Tag.createTag(tagName, tagStatus));
+ return currTags;
+ }
+
+ /**
+ * Merges the current set of tags with a new set of tags sharing the same status,
+ * identified by their tag names, updating the tag status of existing tags in the
+ * process.
+ * {@code updateTagsWithNewTag} method is called on each new tag.
+ */
+ public static Set updateTagsWithNewTags(Set currTags, Set tagNames, TagStatus tagStatus) {
+ tagNames.forEach(x -> updateTagsWithNewTag(currTags, x, tagStatus));
+ return currTags;
+ }
+
+ /**
+ * @param currTags current tag set to be updated.
+ * @param tagName name of the new tag.
+ * @return
+ */
+ public static Set removeTagFromTagSet(Set currTags, String tagName) {
+ // remove the potentially existing Tag of the same name from the hashset.
+ Tag newTag = Tag.createTag(tagName, TagStatus.DEFAULT_STATUS);
+ currTags.remove(newTag);
+ return currTags;
+ }
+
+ /**
+ * @param currTags current tag set to be updated.
+ * @param tagName name of the new tutorial tag.
+ * @return
+ */
+ public static Set removeTutTagFromTagSet(Set currTags, String tagName) {
+ // remove the potentially existing Tag of the same name from the hashset.
+ Tag newTag = Tag.createTag(tagName, TagStatus.DEFAULT_STATUS);
+ currTags.removeIf(tag -> tag.isTutorial() && tag.equals(newTag));
+ return currTags;
+ }
+
+ /**
+ * Removes the tags with the specified tag names from the current set of tags.
+ * @param currTags
+ * @param tagNames
+ * @return
+ */
+ public static Set removeTagsFromTagSet(Set currTags, Set tagNames) {
+ currTags.removeAll(tagNames.stream()
+ .map(x -> Tag.createTag(x, TagStatus.DEFAULT_STATUS))
+ .collect(Collectors.toSet()));
+ return currTags;
+ }
+
+ public boolean isAttendance() {
+ return tagType == TagType.ATTENDANCE;
+ }
+
+ public boolean isAssignment() {
+ return tagType == TagType.ASSIGNMENT;
+ }
+
+ public boolean isTutorial() {
+ return tagType == TagType.TUTORIAL;
+ }
+ public boolean isAssigned() {
+ return isTutorial() && (tagStatus == TagStatus.ASSIGNED);
+ }
+ public boolean isAvailable() {
+ return isTutorial() && (tagStatus == TagStatus.AVAILABLE);
+ }
}
diff --git a/src/main/java/seedu/address/model/tag/TagStatus.java b/src/main/java/seedu/address/model/tag/TagStatus.java
new file mode 100644
index 00000000000..8e36e973d8d
--- /dev/null
+++ b/src/main/java/seedu/address/model/tag/TagStatus.java
@@ -0,0 +1,61 @@
+package seedu.address.model.tag;
+
+/**
+ * Represents submission / attendance status of a tag.
+ */
+public enum TagStatus {
+ COMPLETE_GOOD, // complete before deadline
+ COMPLETE_BAD, // complete after deadline
+ INCOMPLETE_GOOD, // incomplete before deadline
+ INCOMPLETE_BAD, // incomplete after deadline
+
+ PRESENT,
+ ABSENT,
+ ABSENT_WITH_REASON,
+
+ ASSIGNED, AVAILABLE;
+
+ public static final TagStatus DEFAULT_STATUS = INCOMPLETE_GOOD;
+ public static final String COMPLETE_GOOD_KEYWORD = "cg";
+ public static final String COMPLETE_BAD_KEYWORD = "cb";
+ public static final String INCOMPLETE_GOOD_KEYWORD = "ig";
+ public static final String INCOMPLETE_BAD_KEYWORD = "ib";
+ public static final String PRESENT_KEYWORD = "p";
+ public static final String ABSENT_KEYWORD = "a";
+ public static final String ABSENT_WITH_REASON_KEYWORD = "awr";
+ public static final String ASSIGNED_KEYWORD = "as";
+ public static final String AVAILABLE_KEYWORD = "av";
+
+ public static final String INVALID_TAGSTATUS_ERROR_MSG = "Invalid TagStatus Flag, check out the help page for "
+ + "valid TagStatus tags.";
+
+ /**
+ * @param status Keyword corresponding each of the TagStatus.
+ * @return TagStatus matching the keyword.
+ */
+ public static TagStatus getTagStatus(String status) throws IllegalArgumentException {
+ switch (status) {
+ case COMPLETE_GOOD_KEYWORD:
+ return COMPLETE_GOOD;
+ case COMPLETE_BAD_KEYWORD:
+ return COMPLETE_BAD;
+ case INCOMPLETE_GOOD_KEYWORD:
+ return INCOMPLETE_GOOD;
+ case INCOMPLETE_BAD_KEYWORD:
+ return INCOMPLETE_BAD;
+ case PRESENT_KEYWORD:
+ return PRESENT;
+ case ABSENT_KEYWORD:
+ return ABSENT;
+ case ABSENT_WITH_REASON_KEYWORD:
+ return ABSENT_WITH_REASON;
+ case ASSIGNED_KEYWORD:
+ return ASSIGNED;
+ case AVAILABLE_KEYWORD:
+ return AVAILABLE;
+ default:
+ throw new IllegalArgumentException(INVALID_TAGSTATUS_ERROR_MSG);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/tag/TagType.java b/src/main/java/seedu/address/model/tag/TagType.java
new file mode 100644
index 00000000000..a5394dfb6b1
--- /dev/null
+++ b/src/main/java/seedu/address/model/tag/TagType.java
@@ -0,0 +1,11 @@
+package seedu.address.model.tag;
+
+/**
+ * Represents the different types of tags.
+ */
+public enum TagType {
+ ASSIGNMENT,
+ ATTENDANCE,
+ TUTORIAL;
+ public static final TagType DEFAULT_TYPE = ASSIGNMENT;
+}
diff --git a/src/main/java/seedu/address/model/tag/TutorialTag.java b/src/main/java/seedu/address/model/tag/TutorialTag.java
new file mode 100644
index 00000000000..36400296545
--- /dev/null
+++ b/src/main/java/seedu/address/model/tag/TutorialTag.java
@@ -0,0 +1,24 @@
+package seedu.address.model.tag;
+
+/**
+ * Represents a tutorial tag.
+ */
+public class TutorialTag extends Tag {
+ /**
+ * Constructs a {@code Tag}.
+ *
+ * @param tagName A valid tag name.
+ * @param tagStatus A valid tag status.
+ */
+ public TutorialTag(String tagName, TagStatus tagStatus) {
+ super(tagName, tagStatus);
+ }
+
+ public boolean isSameTutorialTag(TutorialTag tutorialTag) {
+ return this.getTagName().equals(tutorialTag.getTagName());
+ }
+ @Override
+ public String toString() {
+ return getTagName();
+ }
+}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 1806da4facf..f0664457e05 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -6,12 +6,17 @@
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.Id;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
+import seedu.address.model.person.PersonType;
import seedu.address.model.person.Phone;
+import seedu.address.model.tag.AssignmentTag;
+import seedu.address.model.tag.AttendanceTag;
import seedu.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TutorialTag;
/**
* Contains utility methods for populating {@code AddressBook} with sample data.
@@ -19,29 +24,54 @@
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"))
+ Person.of(PersonType.STU, new Name("Bernice Yu"), new Id("A9128392K"), new Phone("99272758"),
+ new Email("berniceyu@example.com"), getTagSet(
+ new AssignmentTag("Assignment1", TagStatus.COMPLETE_GOOD),
+ new AssignmentTag("Assignment2", TagStatus.INCOMPLETE_GOOD),
+ new AttendanceTag("Week1", TagStatus.PRESENT),
+ new AttendanceTag("Week2", TagStatus.ABSENT),
+ new TutorialTag("WED09", TagStatus.ASSIGNED))),
+ Person.of(PersonType.TA, new Name("Charlotte Oliveiro"), new Id("A2222222P"), new Phone("93210283"),
+ new Email("charlotte@example.com"), getTagSet(
+ new TutorialTag("THU10", TagStatus.AVAILABLE),
+ new TutorialTag("WED09", TagStatus.ASSIGNED))),
+ Person.of(PersonType.STU, new Name("David Li"), new Id("A9128392Z"), new Phone("91031282"),
+ new Email("lidavid@example.com"), getTagSet(
+ new AssignmentTag("Assignment1", TagStatus.INCOMPLETE_GOOD),
+ new AssignmentTag("Assignment2", TagStatus.INCOMPLETE_BAD),
+ new AttendanceTag("Week1", TagStatus.ABSENT_WITH_REASON),
+ new AttendanceTag("Week2", TagStatus.PRESENT),
+ new TutorialTag("TUES08", TagStatus.ASSIGNED))),
+ Person.of(PersonType.STU, new Name("Irfan Ibrahim"), new Id("B0198266Z"), new Phone("92492021"),
+ new Email("irfan@example.com"), getTagSet(
+ new AssignmentTag("Assignment1", TagStatus.INCOMPLETE_GOOD),
+ new AssignmentTag("Assignment2", TagStatus.INCOMPLETE_GOOD),
+ new AttendanceTag("Week1", TagStatus.ABSENT_WITH_REASON),
+ new AttendanceTag("Week2", TagStatus.PRESENT),
+ new TutorialTag("THU10", TagStatus.ASSIGNED))),
+ Person.of(PersonType.STU, new Name("Roy Balakrishnan"), new Id("B0000666C"), new Phone("92624417"),
+ new Email("royb@example.com"), getTagSet(
+ new AssignmentTag("Assignment1", TagStatus.INCOMPLETE_GOOD),
+ new AssignmentTag("Assignment2", TagStatus.INCOMPLETE_BAD),
+ new AttendanceTag("Week1", TagStatus.PRESENT),
+ new AttendanceTag("Week2", TagStatus.PRESENT),
+ new TutorialTag("THU10", TagStatus.ASSIGNED))),
+ };
+ }
+
+ public static TutorialTag[] getSampleTutorialTags() {
+ return new TutorialTag[] {
+ new TutorialTag("TUE08", TagStatus.AVAILABLE),
+ new TutorialTag("WED09", TagStatus.AVAILABLE),
+ new TutorialTag("THU10", TagStatus.AVAILABLE),
};
}
public static ReadOnlyAddressBook getSampleAddressBook() {
AddressBook sampleAb = new AddressBook();
+ for (TutorialTag sampleTutorialTag : getSampleTutorialTags()) {
+ sampleAb.addTutorialTag(sampleTutorialTag);
+ }
for (Person samplePerson : getSamplePersons()) {
sampleAb.addPerson(samplePerson);
}
@@ -53,8 +83,16 @@ public static ReadOnlyAddressBook getSampleAddressBook() {
*/
public static Set getTagSet(String... strings) {
return Arrays.stream(strings)
- .map(Tag::new)
+ .map(tagName -> Tag.createTag(tagName, TagStatus.DEFAULT_STATUS))
.collect(Collectors.toSet());
}
+ /**
+ * Returns a tag set containing a list of tags.
+ */
+ public static Set getTagSet(Tag... tags) {
+ return Arrays.stream(tags)
+ .map(tag -> Tag.createTag(tag.getTagName(), tag.getTagStatus()))
+ .collect(Collectors.toSet());
+ }
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
index bd1ca0f56c8..e39960d0a71 100644
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
@@ -10,10 +10,11 @@
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.Id;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
+import seedu.address.model.person.PersonType;
import seedu.address.model.person.Phone;
import seedu.address.model.tag.Tag;
@@ -24,23 +25,27 @@ class JsonAdaptedPerson {
public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!";
+ private final String type;
private final String name;
+ private final String id;
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,
+ public JsonAdaptedPerson(@JsonProperty("type") String type, @JsonProperty("name") String name,
+ @JsonProperty("id") String id,
+ @JsonProperty("phone") String phone,
+ @JsonProperty("email") String email,
@JsonProperty("tags") List tags) {
+ this.type = type;
this.name = name;
+ this.id = id;
this.phone = phone;
this.email = email;
- this.address = address;
if (tags != null) {
this.tags.addAll(tags);
}
@@ -50,10 +55,11 @@ public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone
* Converts a given {@code Person} into this class for Jackson use.
*/
public JsonAdaptedPerson(Person source) {
+ type = source.getType().toString();
name = source.getName().fullName;
+ id = source.getId().value;
phone = source.getPhone().value;
email = source.getEmail().value;
- address = source.getAddress().value;
tags.addAll(source.getTags().stream()
.map(JsonAdaptedTag::new)
.collect(Collectors.toList()));
@@ -70,6 +76,15 @@ public Person toModelType() throws IllegalValueException {
personTags.add(tag.toModelType());
}
+ if (type == null) {
+ throw new IllegalValueException(
+ String.format(MISSING_FIELD_MESSAGE_FORMAT, PersonType.class.getSimpleName()));
+ }
+ if (!PersonType.isValidPersonType(type)) {
+ throw new IllegalValueException(PersonType.MESSAGE_CONSTRAINTS);
+ }
+ final PersonType personType = PersonType.getPersonType(type);
+
if (name == null) {
throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
}
@@ -77,6 +92,13 @@ public Person toModelType() throws IllegalValueException {
throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS);
}
final Name modelName = new Name(name);
+ if (id == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Id.class.getSimpleName()));
+ }
+ if (!Id.isValidId(id)) {
+ throw new IllegalValueException(Id.MESSAGE_CONSTRAINTS);
+ }
+ final Id modelId = new Id(id);
if (phone == null) {
throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()));
@@ -94,16 +116,9 @@ public Person toModelType() throws IllegalValueException {
}
final Email modelEmail = new Email(email);
- if (address == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
- }
- 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);
+
+ return Person.of(personType, modelName, modelId, modelPhone, modelEmail, modelTags);
}
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java
index 0df22bdb754..61e31859e08 100644
--- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java
+++ b/src/main/java/seedu/address/storage/JsonAdaptedTag.java
@@ -1,10 +1,12 @@
package seedu.address.storage;
import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonValue;
+import com.fasterxml.jackson.annotation.JsonProperty;
import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TagType;
/**
* Jackson-friendly version of {@link Tag}.
@@ -12,27 +14,46 @@
class JsonAdaptedTag {
private final String tagName;
+ private final TagStatus tagStatus;
+ private final TagType tagType;
/**
* Constructs a {@code JsonAdaptedTag} with the given {@code tagName}.
*/
@JsonCreator
- public JsonAdaptedTag(String tagName) {
+ public JsonAdaptedTag(@JsonProperty("tagName") String tagName,
+ @JsonProperty("tagStatus") TagStatus tagStatus,
+ @JsonProperty("tagType") TagType tagType) {
this.tagName = tagName;
+ this.tagStatus = tagStatus;
+ this.tagType = tagType;
}
/**
* Converts a given {@code Tag} into this class for Jackson use.
*/
public JsonAdaptedTag(Tag source) {
- tagName = source.tagName;
+ tagName = source.getTagName();
+ tagStatus = source.getTagStatus();
+ tagType = source.getTagType();
}
- @JsonValue
+ @JsonProperty("tagName")
public String getTagName() {
return tagName;
}
+ @JsonProperty("tagStatus")
+ public TagStatus getTagStatus() {
+ return tagStatus;
+ }
+
+ @JsonProperty("tagType")
+ public TagType getTagType() {
+ return tagType;
+ }
+
+
/**
* Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object.
*
@@ -42,7 +63,7 @@ public Tag toModelType() throws IllegalValueException {
if (!Tag.isValidTagName(tagName)) {
throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS);
}
- return new Tag(tagName);
+ return Tag.createTag(tagName, tagStatus);
}
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTutorialTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTutorialTag.java
new file mode 100644
index 00000000000..62312e6dad5
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedTutorialTag.java
@@ -0,0 +1,49 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TutorialTag;
+
+/**
+ * Jackson-friendly version of {@link TutorialTag}.
+ */
+class JsonAdaptedTutorialTag {
+
+ private final String tagName;
+
+ /**
+ * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}.
+ */
+ @JsonCreator
+ public JsonAdaptedTutorialTag(@JsonProperty("tagName") String tagName) {
+ this.tagName = tagName;
+ }
+
+ /**
+ * Converts a given {@code TutorialTag} into this class for Jackson use.
+ */
+ public JsonAdaptedTutorialTag(TutorialTag source) {
+ tagName = source.getTagName();
+ }
+
+ @JsonProperty("tagName")
+ public String getTagName() {
+ return tagName;
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted tutorialTag object into the model's {@code TutorialTag} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted tutorialTag.
+ */
+ public TutorialTag toModelType() throws IllegalValueException {
+ if (!TutorialTag.isValidTagName(tagName)) {
+ throw new IllegalValueException(TutorialTag.MESSAGE_CONSTRAINTS);
+ }
+ return new TutorialTag(tagName, TagStatus.AVAILABLE);
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
index 5efd834091d..0f48b3a1bbb 100644
--- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
+++ b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
@@ -12,6 +12,7 @@
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
import seedu.address.model.person.Person;
+import seedu.address.model.tag.TutorialTag;
/**
* An Immutable AddressBook that is serializable to JSON format.
@@ -20,15 +21,19 @@
class JsonSerializableAddressBook {
public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s).";
+ public static final String MESSAGE_DUPLICATE_TUTORIALTAG = "Persons list contains duplicate tutorial tag(s).";
private final List persons = new ArrayList<>();
+ private final List tutorialTags = new ArrayList<>();
/**
- * Constructs a {@code JsonSerializableAddressBook} with the given persons.
+ * Constructs a {@code JsonSerializableAddressBook} with the given persons and tutorialTags.
*/
@JsonCreator
- public JsonSerializableAddressBook(@JsonProperty("persons") List persons) {
+ public JsonSerializableAddressBook(@JsonProperty("persons") List persons,
+ @JsonProperty("tutorialTags") List tutorialTags) {
this.persons.addAll(persons);
+ this.tutorialTags.addAll(tutorialTags);
}
/**
@@ -38,6 +43,8 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
+ public static final String USERGUIDE_URL = "https://ay2324s2-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/address/ui/MainWindow.java
index 79e74ef37c0..7fae7c3b44d 100644
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ b/src/main/java/seedu/address/ui/MainWindow.java
@@ -5,14 +5,18 @@
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.MenuItem;
+import javafx.scene.control.TextField;
import javafx.scene.control.TextInputControl;
+import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
+import javafx.stage.Modality;
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.Command;
import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
@@ -78,6 +82,7 @@ private void setAccelerators() {
/**
* Sets the accelerator of a MenuItem.
+ *
* @param keyCombination the KeyCombination value of the accelerator
*/
private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) {
@@ -147,6 +152,21 @@ public void handleHelp() {
}
}
+ /**
+ * Opens the warning window or focuses on it if it's already opened.
+ */
+ @FXML
+ public void handleWarning(WarningWindow warningWindow) {
+ Stage popupStage = warningWindow.getRoot();
+
+ // ensure that the warningWindow is always focused and must
+ // be interacted before the user can return to the main window
+ popupStage.initModality(Modality.APPLICATION_MODAL);
+ popupStage.initOwner(primaryStage);
+
+ warningWindow.showAndWait();
+ }
+
void show() {
primaryStage.show();
}
@@ -163,34 +183,67 @@ private void handleExit() {
primaryStage.hide();
}
+ /**
+ * Gives the command text field focus and makes it editable
+ * whenever enter or slash keys are pressed.
+ */
+ @FXML
+ private void handleEnterReleased(KeyEvent event) {
+ if (event.getCode() == KeyCode.ENTER || event.getCode() == KeyCode.SLASH) {
+ TextField commandInputField =
+ (TextField) ((StackPane) this.commandBoxPlaceholder.getChildren().get(0)).getChildren().get(0);
+ commandInputField.requestFocus();
+ commandInputField.setEditable(true);
+ }
+ }
+
public PersonListPanel getPersonListPanel() {
return personListPanel;
}
/**
* Executes the command and returns the result.
- *
- * @see seedu.address.logic.Logic#execute(String)
*/
private 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();
+ Command command = logic.parseCommand(commandText);
+
+ if (command.getNeedsWarningPopup()) {
+ WarningWindow warningWindow = new WarningWindow();
+ warningWindow.setMessage(commandText + " - Click OK to confirm.");
+ handleWarning(warningWindow);
+ if (warningWindow.isOkClicked()) {
+ return unsafeExecuteCommand(command);
+ } else {
+ CommandResult commandResult = new CommandResult("Execution of " + commandText
+ + " aborted", false, false);
+ logger.info("Result: " + commandResult.getFeedbackToUser());
+ resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());
+ return commandResult;
+ }
+ } else {
+ return unsafeExecuteCommand(command);
}
-
- if (commandResult.isExit()) {
- handleExit();
- }
-
- return commandResult;
} catch (CommandException | ParseException e) {
logger.info("An error occurred while executing command: " + commandText);
resultDisplay.setFeedbackToUser(e.getMessage());
throw e;
}
}
+
+ private CommandResult unsafeExecuteCommand(Command command) throws CommandException, ParseException {
+ CommandResult commandResult = logic.execute(command);
+ logger.info("Result: " + commandResult.getFeedbackToUser());
+ resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());
+
+ if (commandResult.isShowHelp()) {
+ handleHelp();
+ }
+
+ if (commandResult.isExit()) {
+ handleExit();
+ }
+
+ return commandResult;
+ }
}
diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java
index 094c42cda82..71b3ed4b588 100644
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ b/src/main/java/seedu/address/ui/PersonCard.java
@@ -1,6 +1,7 @@
package seedu.address.ui;
import java.util.Comparator;
+import java.util.function.Predicate;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
@@ -8,6 +9,8 @@
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import seedu.address.model.person.Person;
+import seedu.address.model.person.PersonType;
+import seedu.address.model.tag.Tag;
/**
* An UI component that displays information of a {@code Person}.
@@ -32,14 +35,24 @@ public class PersonCard extends UiPart {
private Label name;
@FXML
private Label id;
+
+ @FXML
+ private Label index;
@FXML
private Label phone;
@FXML
private Label address;
@FXML
private Label email;
+
+ @FXML
+ private Label type;
@FXML
- private FlowPane tags;
+ private FlowPane assignmentTags;
+ @FXML
+ private FlowPane attendanceTags;
+ @FXML
+ private FlowPane tutorialTags;
/**
* Creates a {@code PersonCode} with the given {@code Person} and index to display.
@@ -47,13 +60,27 @@ public class PersonCard extends UiPart {
public PersonCard(Person person, int displayedIndex) {
super(FXML);
this.person = person;
- id.setText(displayedIndex + ". ");
+ index.setText(Integer.toString(displayedIndex));
name.setText(person.getName().fullName);
+ id.setText(person.getId().value);
phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
email.setText(person.getEmail().value);
+ type.setText(person.getType().toString());
+ type.getStyleClass().setAll(person.getType() == PersonType.TA ? "type-ta" : "type-stu");
+ addTagsToContainer(person, Tag::isAssignment, assignmentTags);
+ addTagsToContainer(person, Tag::isAttendance, attendanceTags);
+ addTagsToContainer(person, Tag::isAssigned, tutorialTags);
+ addTagsToContainer(person, Tag::isAvailable, tutorialTags);
+ }
+
+ private void addTagsToContainer(Person person, Predicate filterPredicate, FlowPane container) {
person.getTags().stream()
- .sorted(Comparator.comparing(tag -> tag.tagName))
- .forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
+ .sorted(Comparator.comparing(tag -> tag.getTagName()))
+ .filter(filterPredicate)
+ .forEach(tag -> {
+ Label tagLabel = new Label(tag.getTagName());
+ tagLabel.getStyleClass().addAll("label", tag.getTagStatus().toString().toLowerCase());
+ container.getChildren().add(tagLabel);
+ });
}
}
diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java
index f4c501a897b..cd52e96c8e4 100644
--- a/src/main/java/seedu/address/ui/PersonListPanel.java
+++ b/src/main/java/seedu/address/ui/PersonListPanel.java
@@ -16,7 +16,6 @@
public class PersonListPanel extends UiPart {
private static final String FXML = "PersonListPanel.fxml";
private final Logger logger = LogsCenter.getLogger(PersonListPanel.class);
-
@FXML
private ListView personListView;
diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/address/ui/UiManager.java
index fdf024138bc..ee7cd3c0338 100644
--- a/src/main/java/seedu/address/ui/UiManager.java
+++ b/src/main/java/seedu/address/ui/UiManager.java
@@ -9,7 +9,7 @@
import javafx.stage.Stage;
import seedu.address.MainApp;
import seedu.address.commons.core.LogsCenter;
-import seedu.address.commons.util.StringUtil;
+import seedu.address.commons.util.StatefulStringUtil;
import seedu.address.logic.Logic;
/**
@@ -20,7 +20,7 @@ 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/icon.png";
private Logic logic;
private MainWindow mainWindow;
@@ -45,7 +45,7 @@ public void start(Stage primaryStage) {
mainWindow.fillInnerParts();
} catch (Throwable e) {
- logger.severe(StringUtil.getDetails(e));
+ logger.severe(StatefulStringUtil.getDetails(e));
showFatalErrorDialogAndShutdown("Fatal error during initializing", e);
}
}
@@ -79,7 +79,7 @@ private static void showAlertDialogAndWait(Stage owner, AlertType type, String t
* and exits the application after the user has closed the alert dialog.
*/
private void showFatalErrorDialogAndShutdown(String title, Throwable e) {
- logger.severe(title + " " + e.getMessage() + StringUtil.getDetails(e));
+ logger.severe(title + " " + e.getMessage() + StatefulStringUtil.getDetails(e));
showAlertDialogAndWait(Alert.AlertType.ERROR, title, e.getMessage(), e.toString());
Platform.exit();
System.exit(1);
diff --git a/src/main/java/seedu/address/ui/WarningWindow.java b/src/main/java/seedu/address/ui/WarningWindow.java
new file mode 100644
index 00000000000..897fb9a9075
--- /dev/null
+++ b/src/main/java/seedu/address/ui/WarningWindow.java
@@ -0,0 +1,133 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.fxml.FXML;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.stage.Stage;
+import seedu.address.commons.core.LogsCenter;
+
+/**
+ * This class is the controller for the warning window pop-up.
+ */
+public class WarningWindow extends UiPart {
+
+ private static final Logger logger = LogsCenter.getLogger(WarningWindow.class);
+ private static final String FXML = "WarningWindow.fxml";
+
+ @FXML
+ private Label warningMessage;
+
+ @FXML
+ private Button okButton;
+
+ @FXML
+ private Button cancelButton;
+
+ private boolean isOkClicked = false;
+
+ /**
+ * Constructor for Warning Window controller
+ * @param root
+ */
+ public WarningWindow(Stage root) {
+ super(FXML, root);
+ Scene scene = root.getScene();
+ if (scene != null) {
+ cancelButton.setCancelButton(true);
+ cancelButton.requestFocus();
+ okButton.disarm();
+ cancelButton.arm();
+ } else {
+ logger.warning("Scene is null, unable to set default button.");
+ }
+
+ scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
+ if (event.getCode() == KeyCode.LEFT) {
+ okButton.requestFocus();
+ okButton.arm();
+ cancelButton.disarm();
+ event.consume();
+ }
+ });
+
+ scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
+ if (event.getCode() == KeyCode.RIGHT) {
+ cancelButton.requestFocus();
+ cancelButton.arm();
+ okButton.disarm();
+ event.consume();
+ }
+ });
+
+ scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
+ if (event.getCode() == KeyCode.ENTER) {
+ if (scene != null && scene.getFocusOwner() instanceof Button) {
+ Button focusedButton = (Button) scene.getFocusOwner();
+ focusedButton.fire();
+ event.consume();
+ }
+ }
+ });
+ }
+
+ /**
+ * Creates a new WarningWindow.
+ */
+ public WarningWindow() {
+ this(new Stage());
+ }
+
+ public void setMessage(String message) {
+ warningMessage.setText(message);
+ }
+
+ public boolean isOkClicked() {
+ return isOkClicked;
+ }
+
+ @FXML
+ private void okClicked() {
+ isOkClicked = true;
+ hide();
+ }
+
+ @FXML
+ private void cancelClicked() {
+ hide();
+ }
+
+ /**
+ * Shows warning page and waits for user action.
+ */
+ public void showAndWait() {
+ logger.fine("Showing warning page about the application and waiting.");
+ getRoot().showAndWait();
+ getRoot().centerOnScreen();
+ }
+
+ /**
+ * Returns true if the warning window is currently being shown.
+ */
+ public boolean isShowing() {
+ return getRoot().isShowing();
+ }
+
+ /**
+ * Hides the warning window.
+ */
+ public void hide() {
+ getRoot().hide();
+ }
+
+ /**
+ * Focuses on the warning window.
+ */
+ public void focus() {
+ getRoot().requestFocus();
+ }
+}
diff --git a/src/main/resources/images/address_book_32.png b/src/main/resources/images/address_book_32.png
deleted file mode 100644
index 29810cf1fd9..00000000000
Binary files a/src/main/resources/images/address_book_32.png and /dev/null differ
diff --git a/src/main/resources/images/icon.png b/src/main/resources/images/icon.png
new file mode 100644
index 00000000000..bf404254708
Binary files /dev/null and b/src/main/resources/images/icon.png differ
diff --git a/src/main/resources/images/warning_icon.png b/src/main/resources/images/warning_icon.png
new file mode 100644
index 00000000000..440c334f016
Binary files /dev/null and b/src/main/resources/images/warning_icon.png differ
diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml
index 124283a392e..f3b1384fe62 100644
--- a/src/main/resources/view/CommandBox.fxml
+++ b/src/main/resources/view/CommandBox.fxml
@@ -1,9 +1,10 @@
-
-
+
+
-
-
+
+
-
diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css
index 36e6b001cd8..7aafbde0d3c 100644
--- a/src/main/resources/view/DarkTheme.css
+++ b/src/main/resources/view/DarkTheme.css
@@ -90,7 +90,7 @@
.list-view {
-fx-background-insets: 0;
-fx-padding: 0;
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: #e0e0e0;
}
.list-cell {
@@ -100,21 +100,26 @@
}
.list-cell:filled:even {
- -fx-background-color: #3c3e3f;
+ -fx-background-color: white;
+ -fx-background-radius: 15;
}
.list-cell:filled:odd {
- -fx-background-color: #515658;
+ -fx-background-color: white;
+ -fx-background-radius: 15;
}
.list-cell:filled:selected {
- -fx-background-color: #424d5f;
+ -fx-background-color: #b6c2d6;
+ -fx-background-radius: 15;
}
+/*
.list-cell:filled:selected #cardPane {
- -fx-border-color: #3e7b91;
- -fx-border-width: 1;
-}
+ -fx-border-color: black;
+ -fx-border-width: 2;
+ -fx-border-radius: 15;
+}*/
.list-cell .label {
-fx-text-fill: white;
@@ -147,19 +152,23 @@
}
.result-display {
- -fx-background-color: transparent;
+ -fx-padding: 1;
+ -fx-background-color: #e0e0e0;
+ -fx-border-color: #383838;
+ -fx-border-width: 1;
+ -fx-border-radius: 5;
-fx-font-family: "Segoe UI Light";
-fx-font-size: 13pt;
- -fx-text-fill: white;
+ -fx-text-fill: black;
}
.result-display .label {
- -fx-text-fill: black !important;
+ -fx-text-fill: black;
}
.status-bar .label {
-fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
+ -fx-text-fill: black;
-fx-padding: 4px;
-fx-pref-height: 30px;
}
@@ -171,7 +180,7 @@
}
.status-bar-with-border .label {
- -fx-text-fill: white;
+ -fx-text-fill: black;
}
.grid-pane {
@@ -189,17 +198,17 @@
}
.context-menu .label {
- -fx-text-fill: white;
+ -fx-text-fill: black;
}
.menu-bar {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: #e0e0e0;
}
.menu-bar .label {
-fx-font-size: 14pt;
-fx-font-family: "Segoe UI Light";
- -fx-text-fill: white;
+ -fx-text-fill: black;
-fx-opacity: 0.9;
}
@@ -282,11 +291,11 @@
}
.scroll-bar {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: #e0e0e0;
}
.scroll-bar .thumb {
- -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-color: grey;
-fx-background-insets: 3;
}
@@ -307,9 +316,28 @@
-fx-padding: 8 1 8 1;
}
+.type-stu {
+ -fx-text-fill: white;
+ -fx-background-color: #e38314;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 5;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
+
+.type-ta {
+ -fx-text-fill: white;
+ -fx-background-color: #1d913c;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 5;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
+
#cardPane {
- -fx-background-color: transparent;
- -fx-border-width: 0;
+ -fx-border-width: 5;
+ -fx-border-color: #e0e0e0;
+ -fx-border-radius: 10;
}
#commandTypeLabel {
@@ -318,14 +346,14 @@
}
#commandTextField {
- -fx-background-color: transparent #383838 transparent #383838;
+ -fx-background-color: transparent;
-fx-background-insets: 0;
- -fx-border-color: #383838 #383838 #ffffff #383838;
+ -fx-border-color: #383838;
-fx-border-insets: 0;
-fx-border-width: 1;
-fx-font-family: "Segoe UI Light";
-fx-font-size: 13pt;
- -fx-text-fill: white;
+ -fx-text-fill: black;
}
#filterField, #personListPanel, #personWebpage {
@@ -333,16 +361,16 @@
}
#resultDisplay .content {
- -fx-background-color: transparent, #383838, transparent, #383838;
+ -fx-background-color: #e0e0e0;
-fx-background-radius: 0;
}
-#tags {
+#assignmentTags {
-fx-hgap: 7;
-fx-vgap: 3;
}
-#tags .label {
+#assignmentTags .label {
-fx-text-fill: white;
-fx-background-color: #3e7b91;
-fx-padding: 1 3 1 3;
@@ -350,3 +378,71 @@
-fx-background-radius: 2;
-fx-font-size: 11;
}
+
+#assignmentTags .label.complete_good {
+ -fx-background-color: green; /* Green color for tags with complete_good status */
+}
+
+#assignmentTags .label.complete_bad {
+ -fx-background-color: orange; /* Red color for tags with complete_bad status */
+}
+
+#assignmentTags .label.incomplete_good {
+ -fx-background-color: grey; /* Blue color for tags with incomplete_good status */
+}
+
+#assignmentTags .label.incomplete_bad {
+ -fx-background-color: red; /* Orange color for tags with incomplete_bad status */
+}
+
+#attendanceTags {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#attendanceTags .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;
+}
+
+#attendanceTags .label.present {
+ -fx-background-color: green; /* Green color for tags with complete_good status */
+}
+
+#attendanceTags .label.absent {
+ -fx-background-color: red; /* Red color for tags with complete_bad status */
+}
+
+#attendanceTags .label.absent_with_reason {
+ -fx-background-color: orange; /* Blue color for tags with incomplete_good status */
+}
+
+#tutorialTags {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#tutorialTags .label {
+ -fx-border-color: #3e7b91;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
+
+#tutorialTags .label.assigned {
+ -fx-background-color: #3e7b91; /* Dark blue color for tags with assigned status */
+ -fx-text-fill: white /* White tex fill for tags with assigned status */
+}
+
+#tutorialTags .label.available {
+ -fx-background-color: white; /* White color for tags with assigned status */
+ -fx-text-fill: black /* Black text fill for tags with assigned status */
+}
+
+
+
diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css
index bfe82a85964..8e90441204b 100644
--- a/src/main/resources/view/Extensions.css
+++ b/src/main/resources/view/Extensions.css
@@ -1,16 +1,16 @@
.error {
- -fx-text-fill: #d06651 !important; /* The error class should always override the default text-fill style */
+ -fx-text-fill: #c92808 !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;
+ -fx-background: #e0e0e0;
}
.tag-selector {
-fx-border-width: 1;
- -fx-border-color: white;
+ -fx-border-color: grey;
-fx-border-radius: 3;
-fx-background-radius: 3;
}
diff --git a/src/main/resources/view/HelpWindow.css b/src/main/resources/view/HelpWindow.css
index 17e8a8722cd..39898033dbf 100644
--- a/src/main/resources/view/HelpWindow.css
+++ b/src/main/resources/view/HelpWindow.css
@@ -1,19 +1,24 @@
#copyButton, #helpMessage {
- -fx-text-fill: white;
+ -fx-text-fill: black;
}
#copyButton {
- -fx-background-color: dimgray;
+ -fx-background-color: #e0e0e0;
}
#copyButton:hover {
- -fx-background-color: gray;
+ -fx-background-color: grey;
+}
+
+#copyButton:focused{
+ -fx-border-color: #3e7b91;
+ -fx-border-radius: 2;
}
#copyButton:armed {
- -fx-background-color: darkgray;
+ -fx-background-color: grey;
}
#helpMessageContainer {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: white;
}
diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml
index e01f330de33..19e333882e0 100644
--- a/src/main/resources/view/HelpWindow.fxml
+++ b/src/main/resources/view/HelpWindow.fxml
@@ -26,7 +26,7 @@
-
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
index 7778f666a0a..2e7cd35a772 100644
--- a/src/main/resources/view/MainWindow.fxml
+++ b/src/main/resources/view/MainWindow.fxml
@@ -6,15 +6,14 @@
-
+
-
+
-
+
@@ -23,8 +22,8 @@
-
-
+
+
@@ -33,24 +32,23 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml
index f5e812e25e6..a0b8e74b793 100644
--- a/src/main/resources/view/PersonListCard.fxml
+++ b/src/main/resources/view/PersonListCard.fxml
@@ -2,6 +2,7 @@
+
@@ -12,25 +13,37 @@
+
+
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml
index 01b691792a9..6d76e2ca9f9 100644
--- a/src/main/resources/view/ResultDisplay.fxml
+++ b/src/main/resources/view/ResultDisplay.fxml
@@ -3,7 +3,7 @@
-
-
+
+
diff --git a/src/main/resources/view/WarningWindow.css b/src/main/resources/view/WarningWindow.css
new file mode 100644
index 00000000000..a6ba623a2bb
--- /dev/null
+++ b/src/main/resources/view/WarningWindow.css
@@ -0,0 +1,39 @@
+#okButton, #cancelButton, #warningMessage {
+ -fx-text-fill: black;
+}
+
+#okButton, #cancelButton {
+ -fx-background-color: #e0e0e0;
+}
+
+#okButton:hover {
+ -fx-background-color: grey;
+}
+
+#cancelButton:hover {
+ -fx-background-color: grey;
+}
+
+
+#okButton:focused {
+ -fx-border-color: #3e7b91;
+ -fx-border-radius: 2;
+}
+
+
+#cancelButton:focused {
+ -fx-border-color: #3e7b91;
+ -fx-border-radius: 2;
+}
+
+#okButton:armed {
+ -fx-background-color: grey;
+}
+
+#cancelButton:armed {
+ -fx-background-color: grey;
+}
+
+#warningMessageContainer {
+ -fx-background-color: white;
+}
diff --git a/src/main/resources/view/WarningWindow.fxml b/src/main/resources/view/WarningWindow.fxml
new file mode 100644
index 00000000000..128437c6a70
--- /dev/null
+++ b/src/main/resources/view/WarningWindow.fxml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
index 6a4d2b7181c..eb43acdfac1 100644
--- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
+++ b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
@@ -9,5 +9,8 @@
"phone": "948asdf2424",
"email": "hans@example.com",
"address": "4th street"
+ } ],
+ "tutorialTags" : [ {
+ "tagName" : "WED11"
} ]
}
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
index ccd21f7d1a9..738dcd2ac31 100644
--- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
+++ b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
@@ -4,5 +4,8 @@
"phone": "9482424",
"email": "hans@example.com",
"address": "4th street"
+ } ],
+ "tutorialTags" : [ {
+ "tagName" : "WED11"
} ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
index a7427fe7aa2..d2b6bf92c6c 100644
--- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
@@ -1,14 +1,22 @@
{
"persons": [ {
+ "type" : "stu",
"name": "Alice Pauline",
"phone": "94351253",
+ "id" : "A0111111A",
"email": "alice@example.com",
"address": "123, Jurong West Ave 6, #08-111",
- "tags": [ "friends" ]
+ "tags" : [ {
+ "tagName" : "friends",
+ "tagStatus" : "INCOMPLETE_GOOD"
+ } ]
}, {
- "name": "Alice Pauline",
+ "type" : "stu",
+ "name": "Alicia Pauline",
+ "id" : "A0111111A",
"phone": "94351253",
"email": "pauline@example.com",
"address": "4th street"
- } ]
+ } ],
+ "tutorialTags" : [ ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicateTutorialTagsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicateTutorialTagsAddressBook.json
new file mode 100644
index 00000000000..94e856b9d43
--- /dev/null
+++ b/src/test/data/JsonSerializableAddressBookTest/duplicateTutorialTagsAddressBook.json
@@ -0,0 +1,8 @@
+{
+ "persons": [],
+ "tutorialTags" : [ {
+ "tagName" : "WED10"
+ }, {
+ "tagName" : "WED10"
+ } ]
+}
diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
index ad3f135ae42..57187e466ac 100644
--- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
@@ -1,8 +1,10 @@
{
"persons": [ {
+ "type" : "stu",
"name": "Hans Muster",
"phone": "9482424",
"email": "invalid@email!3e",
"address": "4th street"
- } ]
+ } ],
+ "tutorialTags" : [ ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidTutorialTagsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidTutorialTagsAddressBook.json
new file mode 100644
index 00000000000..ba317ea727a
--- /dev/null
+++ b/src/test/data/JsonSerializableAddressBookTest/invalidTutorialTagsAddressBook.json
@@ -0,0 +1,8 @@
+{
+ "persons": [],
+ "tutorialTags" : [ {
+ "tagName" : "WED10"
+ }, {
+ "tagName" : "*&jhj"
+ } ]
+}
diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
index 72262099d35..7f519543859 100644
--- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
@@ -1,46 +1,73 @@
{
"_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()",
"persons" : [ {
+ "type" : "stu",
"name" : "Alice Pauline",
+ "id" : "A0111111A",
"phone" : "94351253",
"email" : "alice@example.com",
"address" : "123, Jurong West Ave 6, #08-111",
- "tags" : [ "friends" ]
+ "tags" : [ {
+ "tagName" : "friends",
+ "tagStatus" : "INCOMPLETE_GOOD"
+ } ]
}, {
+ "type" : "stu",
"name" : "Benson Meier",
+ "id" : "A0111111C",
"phone" : "98765432",
"email" : "johnd@example.com",
"address" : "311, Clementi Ave 2, #02-25",
- "tags" : [ "owesMoney", "friends" ]
+ "tags" : [ {
+ "tagName" : "owesMoney",
+ "tagStatus" : "INCOMPLETE_GOOD"
+ }, {
+ "tagName" : "friends",
+ "tagStatus" : "INCOMPLETE_GOOD"
+ } ]
}, {
+ "type" : "stu",
"name" : "Carl Kurz",
+ "id" : "A2222222D",
"phone" : "95352563",
"email" : "heinz@example.com",
"address" : "wall street",
"tags" : [ ]
}, {
+ "type" : "stu",
"name" : "Daniel Meier",
+ "id" : "B3334444E",
"phone" : "87652533",
"email" : "cornelia@example.com",
"address" : "10th street",
- "tags" : [ "friends" ]
+ "tags" : [ {
+ "tagName" : "friends",
+ "tagStatus" : "INCOMPLETE_GOOD"
+ } ]
}, {
+ "type" : "stu",
"name" : "Elle Meyer",
+ "id" : "B3334444F",
"phone" : "9482224",
"email" : "werner@example.com",
"address" : "michegan ave",
"tags" : [ ]
}, {
+ "type" : "ta",
"name" : "Fiona Kunz",
+ "id" : "B5678012F",
"phone" : "9482427",
"email" : "lydia@example.com",
"address" : "little tokyo",
"tags" : [ ]
}, {
+ "type" : "ta",
"name" : "George Best",
+ "id" : "A0000000X",
"phone" : "9482442",
"email" : "anna@example.com",
"address" : "4th street",
"tags" : [ ]
- } ]
+ } ],
+ "tutorialTags" : [ ]
}
diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/seedu/address/commons/util/AppUtilTest.java
index 594de1e6365..3588992315b 100644
--- a/src/test/java/seedu/address/commons/util/AppUtilTest.java
+++ b/src/test/java/seedu/address/commons/util/AppUtilTest.java
@@ -9,7 +9,7 @@ public class AppUtilTest {
@Test
public void getImage_exitingImage() {
- assertNotNull(AppUtil.getImage("/images/address_book_32.png"));
+ assertNotNull(AppUtil.getImage("/images/icon.png"));
}
@Test
diff --git a/src/test/java/seedu/address/commons/util/StatefulStringUtilTest.java b/src/test/java/seedu/address/commons/util/StatefulStringUtilTest.java
new file mode 100644
index 00000000000..6967049770e
--- /dev/null
+++ b/src/test/java/seedu/address/commons/util/StatefulStringUtilTest.java
@@ -0,0 +1,162 @@
+package seedu.address.commons.util;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalTutorialTag.WED10;
+
+import java.io.FileNotFoundException;
+
+import org.junit.jupiter.api.Test;
+
+
+public class StatefulStringUtilTest {
+ //---------------- Tests for isNonZeroUnsignedInteger --------------------------------------
+ protected StatefulStringUtil testStatefulStringUtil;
+
+
+ @Test
+ public void isNonZeroUnsignedInteger() {
+
+ // EP: empty strings
+ assertFalse(StatefulStringUtil.isNonZeroUnsignedInteger("")); // Boundary value
+ assertFalse(StatefulStringUtil.isNonZeroUnsignedInteger(" "));
+
+ // EP: not a number
+ assertFalse(StatefulStringUtil.isNonZeroUnsignedInteger("a"));
+ assertFalse(StatefulStringUtil.isNonZeroUnsignedInteger("aaa"));
+
+ // EP: zero
+ assertFalse(StatefulStringUtil.isNonZeroUnsignedInteger("0"));
+
+ // EP: zero as prefix
+ assertTrue(StatefulStringUtil.isNonZeroUnsignedInteger("01"));
+
+ // EP: signed numbers
+ assertFalse(StatefulStringUtil.isNonZeroUnsignedInteger("-1"));
+ assertFalse(StatefulStringUtil.isNonZeroUnsignedInteger("+1"));
+
+ // EP: numbers with white space
+ assertFalse(StatefulStringUtil.isNonZeroUnsignedInteger(" 10 ")); // Leading/trailing spaces
+ assertFalse(StatefulStringUtil.isNonZeroUnsignedInteger("1 0")); // Spaces in the middle
+
+ // EP: number larger than Integer.MAX_VALUE
+ assertFalse(StatefulStringUtil.isNonZeroUnsignedInteger(Long.toString(Integer.MAX_VALUE + 1)));
+
+ // EP: valid numbers, should return true
+ assertTrue(StatefulStringUtil.isNonZeroUnsignedInteger("1")); // Boundary value
+ assertTrue(StatefulStringUtil.isNonZeroUnsignedInteger("10"));
+ }
+
+
+ //---------------- Tests for containsWordIgnoreCase --------------------------------------
+
+ /*
+ * Invalid equivalence partitions for word: null, empty, multiple words
+ * Invalid equivalence partitions for sentence: null
+ * The four test cases below test one invalid input at a time.
+ */
+
+ @Test
+ public void containsWordIgnoreCase_nullWord_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> StatefulStringUtil
+ .containsSubwordIgnoreCase("typical sentence", null));
+ }
+
+ @Test
+ public void containsWordIgnoreCase_emptyWord_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, "Word parameter cannot be empty", ()
+ -> StatefulStringUtil.containsSubwordIgnoreCase("typical sentence", " "));
+ }
+
+ @Test
+ public void containsWordIgnoreCase_multipleWords_throwsIllegalArgumentException() {
+ assertThrows(IllegalArgumentException.class, "Word parameter should be a single word", ()
+ -> StatefulStringUtil.containsSubwordIgnoreCase("typical sentence", "aaa BBB"));
+ }
+
+ @Test
+ public void containsWordIgnoreCase_nullSentence_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> StatefulStringUtil
+ .containsSubwordIgnoreCase(null, "abc"));
+ }
+
+ /*
+ * Valid equivalence partitions for word:
+ * - any word
+ * - word containing symbols/numbers
+ * - word with leading/trailing spaces
+ *
+ * Valid equivalence partitions for sentence:
+ * - empty string
+ * - one word
+ * - multiple words
+ * - sentence with extra spaces
+ *
+ * Possible scenarios returning true:
+ * - matches first word in sentence
+ * - last word in sentence
+ * - middle word in sentence
+ * - matches multiple words
+ *
+ * Possible scenarios returning false:
+ * - query word matches part of a sentence word
+ * - sentence word matches part of the query word
+ *
+ * The test method below tries to verify all above with a reasonably low number of test cases.
+ */
+
+ @Test
+ public void containsWordIgnoreCase_validInputs_correctResult() {
+
+ // Empty sentence
+ assertFalse(StatefulStringUtil.containsSubwordIgnoreCase("", "abc")); // Boundary case
+ assertFalse(StatefulStringUtil.containsSubwordIgnoreCase(" ", "123"));
+
+ // Matches a partial word only
+ assertTrue(StatefulStringUtil
+ .containsSubwordIgnoreCase("aaa bbb ccc", "bb")); // Sentence word bigger than query word
+ assertFalse(StatefulStringUtil
+ .containsSubwordIgnoreCase("aaa bbb ccc", "bbbb")); // Query word bigger than sentence word
+
+ // Matches word in the sentence, different upper/lower case letters
+ assertTrue(StatefulStringUtil
+ .containsSubwordIgnoreCase("aaa bBb ccc", "Bbb")); // First word (boundary case)
+ assertTrue(StatefulStringUtil
+ .containsSubwordIgnoreCase("aaa bBb ccc@1", "CCc@1")); // Last word (boundary case)
+ assertTrue(StatefulStringUtil
+ .containsSubwordIgnoreCase(" AAA bBb ccc ", "aaa")); // Sentence has extra spaces
+ assertTrue(StatefulStringUtil
+ .containsSubwordIgnoreCase("Aaa", "aaa")); // Only one word in sentence (boundary case)
+ assertTrue(StatefulStringUtil
+ .containsSubwordIgnoreCase("aaa bbb ccc", " ccc ")); // Leading/trailing spaces
+
+ // Matches multiple words in sentence
+ assertTrue(StatefulStringUtil.containsSubwordIgnoreCase("AAA bBb ccc bbb", "bbB"));
+ }
+
+ //---------------- Tests for getDetails --------------------------------------
+
+ /*
+ * Equivalence Partitions: null, valid throwable object
+ */
+
+ @Test
+ public void getDetails_exceptionGiven() {
+ assertTrue(StatefulStringUtil.getDetails(new FileNotFoundException("file not found"))
+ .contains("java.io.FileNotFoundException: file not found"));
+ }
+
+ @Test
+ public void getDetails_nullGiven_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> StatefulStringUtil.getDetails(null));
+ }
+
+
+ //---------------- Tests for tagContainsWordIgnoreCase --------------------------------------
+
+ @Test
+ public void testTagContainsWordIgnoreCase_nullWord_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> testStatefulStringUtil.tagContainsWordIgnoreCase(WED10, null));
+ }
+}
diff --git a/src/test/java/seedu/address/commons/util/StringListUtilTest.java b/src/test/java/seedu/address/commons/util/StringListUtilTest.java
new file mode 100644
index 00000000000..ad8a1d177cb
--- /dev/null
+++ b/src/test/java/seedu/address/commons/util/StringListUtilTest.java
@@ -0,0 +1,16 @@
+package seedu.address.commons.util;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+public class StringListUtilTest {
+ @Test
+ public void separateWithSpaces_stringListWithSpaces_stringListSeparatedBySpace() {
+ String[] strArr = {"alice Bob", "terry", "Gavin"};
+ String[] expectedStrArr = {"alice", "Bob", "terry", "Gavin"};
+ assertArrayEquals(StringListUtil.separateWithSpaces(Arrays.asList(strArr)).toArray(), expectedStrArr);
+ }
+}
diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/seedu/address/commons/util/StringUtilTest.java
deleted file mode 100644
index c56d407bf3f..00000000000
--- a/src/test/java/seedu/address/commons/util/StringUtilTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package seedu.address.commons.util;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.testutil.Assert.assertThrows;
-
-import java.io.FileNotFoundException;
-
-import org.junit.jupiter.api.Test;
-
-public class StringUtilTest {
-
- //---------------- Tests for isNonZeroUnsignedInteger --------------------------------------
-
- @Test
- public void isNonZeroUnsignedInteger() {
-
- // EP: empty strings
- assertFalse(StringUtil.isNonZeroUnsignedInteger("")); // Boundary value
- assertFalse(StringUtil.isNonZeroUnsignedInteger(" "));
-
- // EP: not a number
- assertFalse(StringUtil.isNonZeroUnsignedInteger("a"));
- assertFalse(StringUtil.isNonZeroUnsignedInteger("aaa"));
-
- // EP: zero
- assertFalse(StringUtil.isNonZeroUnsignedInteger("0"));
-
- // EP: zero as prefix
- assertTrue(StringUtil.isNonZeroUnsignedInteger("01"));
-
- // EP: signed numbers
- assertFalse(StringUtil.isNonZeroUnsignedInteger("-1"));
- assertFalse(StringUtil.isNonZeroUnsignedInteger("+1"));
-
- // EP: numbers with white space
- assertFalse(StringUtil.isNonZeroUnsignedInteger(" 10 ")); // Leading/trailing spaces
- assertFalse(StringUtil.isNonZeroUnsignedInteger("1 0")); // Spaces in the middle
-
- // EP: number larger than Integer.MAX_VALUE
- assertFalse(StringUtil.isNonZeroUnsignedInteger(Long.toString(Integer.MAX_VALUE + 1)));
-
- // EP: valid numbers, should return true
- assertTrue(StringUtil.isNonZeroUnsignedInteger("1")); // Boundary value
- assertTrue(StringUtil.isNonZeroUnsignedInteger("10"));
- }
-
-
- //---------------- Tests for containsWordIgnoreCase --------------------------------------
-
- /*
- * Invalid equivalence partitions for word: null, empty, multiple words
- * Invalid equivalence partitions for sentence: null
- * The four test cases below test one invalid input at a time.
- */
-
- @Test
- public void containsWordIgnoreCase_nullWord_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> StringUtil.containsWordIgnoreCase("typical sentence", null));
- }
-
- @Test
- public void containsWordIgnoreCase_emptyWord_throwsIllegalArgumentException() {
- assertThrows(IllegalArgumentException.class, "Word parameter cannot be empty", ()
- -> StringUtil.containsWordIgnoreCase("typical sentence", " "));
- }
-
- @Test
- public void containsWordIgnoreCase_multipleWords_throwsIllegalArgumentException() {
- assertThrows(IllegalArgumentException.class, "Word parameter should be a single word", ()
- -> StringUtil.containsWordIgnoreCase("typical sentence", "aaa BBB"));
- }
-
- @Test
- public void containsWordIgnoreCase_nullSentence_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> StringUtil.containsWordIgnoreCase(null, "abc"));
- }
-
- /*
- * Valid equivalence partitions for word:
- * - any word
- * - word containing symbols/numbers
- * - word with leading/trailing spaces
- *
- * Valid equivalence partitions for sentence:
- * - empty string
- * - one word
- * - multiple words
- * - sentence with extra spaces
- *
- * Possible scenarios returning true:
- * - matches first word in sentence
- * - last word in sentence
- * - middle word in sentence
- * - matches multiple words
- *
- * Possible scenarios returning false:
- * - query word matches part of a sentence word
- * - sentence word matches part of the query word
- *
- * The test method below tries to verify all above with a reasonably low number of test cases.
- */
-
- @Test
- public void containsWordIgnoreCase_validInputs_correctResult() {
-
- // Empty sentence
- assertFalse(StringUtil.containsWordIgnoreCase("", "abc")); // Boundary case
- assertFalse(StringUtil.containsWordIgnoreCase(" ", "123"));
-
- // Matches a partial word only
- assertFalse(StringUtil.containsWordIgnoreCase("aaa bbb ccc", "bb")); // Sentence word bigger than query word
- assertFalse(StringUtil.containsWordIgnoreCase("aaa bbb ccc", "bbbb")); // Query word bigger than sentence word
-
- // Matches word in the sentence, different upper/lower case letters
- assertTrue(StringUtil.containsWordIgnoreCase("aaa bBb ccc", "Bbb")); // First word (boundary case)
- assertTrue(StringUtil.containsWordIgnoreCase("aaa bBb ccc@1", "CCc@1")); // Last word (boundary case)
- assertTrue(StringUtil.containsWordIgnoreCase(" AAA bBb ccc ", "aaa")); // Sentence has extra spaces
- assertTrue(StringUtil.containsWordIgnoreCase("Aaa", "aaa")); // Only one word in sentence (boundary case)
- assertTrue(StringUtil.containsWordIgnoreCase("aaa bbb ccc", " ccc ")); // Leading/trailing spaces
-
- // Matches multiple words in sentence
- assertTrue(StringUtil.containsWordIgnoreCase("AAA bBb ccc bbb", "bbB"));
- }
-
- //---------------- Tests for getDetails --------------------------------------
-
- /*
- * Equivalence Partitions: null, valid throwable object
- */
-
- @Test
- public void getDetails_exceptionGiven() {
- assertTrue(StringUtil.getDetails(new FileNotFoundException("file not found"))
- .contains("java.io.FileNotFoundException: file not found"));
- }
-
- @Test
- public void getDetails_nullGiven_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> StringUtil.getDetails(null));
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java
index baf8ce336a2..bae9478ef73 100644
--- a/src/test/java/seedu/address/logic/LogicManagerTest.java
+++ b/src/test/java/seedu/address/logic/LogicManagerTest.java
@@ -1,14 +1,15 @@
package seedu.address.logic;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.ID_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.AMY;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
@@ -18,10 +19,13 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
+import seedu.address.commons.util.StatefulStringUtil;
import seedu.address.logic.commands.AddCommand;
import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.commands.ListCommand;
import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.logic.parser.StatefulParserUtil;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
@@ -60,8 +64,9 @@ public void execute_invalidCommandFormat_throwsParseException() {
@Test
public void execute_commandExecutionError_throwsCommandException() {
+ initializeStatefulUtils();
String deleteCommand = "delete 9";
- assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ assertParseException(deleteCommand, String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE));
}
@Test
@@ -87,6 +92,15 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException
assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0));
}
+ /**
+ * Initializes the singleton classes with sample model for testing.
+ */
+ private void initializeStatefulUtils() {
+ Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ StatefulParserUtil.initialize(model);
+ StatefulStringUtil.initialize(model);
+ }
+
/**
* Executes the command and confirms that
* - no exceptions are thrown
@@ -96,7 +110,7 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException
*/
private void assertCommandSuccess(String inputCommand, String expectedMessage,
Model expectedModel) throws CommandException, ParseException {
- CommandResult result = logic.execute(inputCommand);
+ CommandResult result = logic.execute(logic.parseCommand(inputCommand));
assertEquals(expectedMessage, result.getFeedbackToUser());
assertEquals(expectedModel, model);
}
@@ -136,7 +150,7 @@ private void assertCommandFailure(String inputCommand, Class extends Throwable
*/
private void assertCommandFailure(String inputCommand, Class extends Throwable> expectedException,
String expectedMessage, Model expectedModel) {
- assertThrows(expectedException, expectedMessage, () -> logic.execute(inputCommand));
+ assertThrows(expectedException, expectedMessage, () -> logic.execute(logic.parseCommand(inputCommand)));
assertEquals(expectedModel, model);
}
@@ -165,8 +179,8 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath)
logic = new LogicManager(model, storage);
// Triggers the saveAddressBook method by executing an add command
- String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY
- + EMAIL_DESC_AMY + ADDRESS_DESC_AMY;
+ String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + ID_DESC_AMY + PHONE_DESC_AMY
+ + EMAIL_DESC_AMY;
Person expectedPerson = new PersonBuilder(AMY).withTags().build();
ModelManager expectedModel = new ModelManager();
expectedModel.addPerson(expectedPerson);
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
index 90e8253f48e..a9c9053bae2 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
@@ -10,6 +10,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.function.Predicate;
import org.junit.jupiter.api.Test;
@@ -23,6 +24,7 @@
import seedu.address.model.ReadOnlyAddressBook;
import seedu.address.model.ReadOnlyUserPrefs;
import seedu.address.model.person.Person;
+import seedu.address.model.tag.TutorialTag;
import seedu.address.testutil.PersonBuilder;
public class AddCommandTest {
@@ -32,6 +34,15 @@ public void constructor_nullPerson_throwsNullPointerException() {
assertThrows(NullPointerException.class, () -> new AddCommand(null));
}
+ @Test
+ public void execute_getNeedsWarningPopup_returnsFalse() {
+ Person validPerson = new PersonBuilder().build();
+
+ AddCommand addCommand = new AddCommand(validPerson);
+
+ assertFalse(addCommand.getNeedsWarningPopup());
+ }
+
@Test
public void execute_personAcceptedByModel_addSuccessful() throws Exception {
ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded();
@@ -55,8 +66,8 @@ public void execute_duplicatePerson_throwsCommandException() {
@Test
public void equals() {
- Person alice = new PersonBuilder().withName("Alice").build();
- Person bob = new PersonBuilder().withName("Bob").build();
+ Person alice = new PersonBuilder().withId("A0123456B").build();
+ Person bob = new PersonBuilder().withId("A0012345B").build();
AddCommand addAliceCommand = new AddCommand(alice);
AddCommand addBobCommand = new AddCommand(bob);
@@ -148,6 +159,11 @@ public void setPerson(Person target, Person editedPerson) {
throw new AssertionError("This method should not be called.");
}
+ @Override
+ public ObservableList getPersonList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
@Override
public ObservableList getFilteredPersonList() {
throw new AssertionError("This method should not be called.");
@@ -157,6 +173,33 @@ public ObservableList getFilteredPersonList() {
public void updateFilteredPersonList(Predicate predicate) {
throw new AssertionError("This method should not be called.");
}
+
+ @Override
+ public void persistentUpdateFilteredList(List extends Predicate> predicates) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteTutorialTag(TutorialTag target) {
+ throw new AssertionError("This method should not be called.");
+ };
+ @Override
+ public void addTutorialTag(TutorialTag tutorialTag) {
+ throw new AssertionError("This method should not be called.");
+ };
+ @Override
+ public boolean hasTutorialTag(TutorialTag tutorialTag) {
+ throw new AssertionError("This method should not be called.");
+ };
+ @Override
+ public ObservableList getTutorialTagList() {
+ throw new AssertionError("This method should not be called.");
+ };
+
+ @Override
+ public String getTutorialTagListString() {
+ throw new AssertionError("This method should not be called");
+ }
}
/**
diff --git a/src/test/java/seedu/address/logic/commands/AvailableCommandTest.java b/src/test/java/seedu/address/logic/commands/AvailableCommandTest.java
new file mode 100644
index 00000000000..2b15b3aa0d8
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AvailableCommandTest.java
@@ -0,0 +1,57 @@
+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.testutil.TypicalTutorialTag.THU10;
+import static seedu.address.testutil.TypicalTutorialTag.WED10;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.person.TutorialTagContainsGroupPredicate;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TutorialTag;
+
+public class AvailableCommandTest {
+
+ @Test
+ public void equals() {
+ TutorialTagContainsGroupPredicate predicate1 = new TutorialTagContainsGroupPredicate("WED10");
+ TutorialTagContainsGroupPredicate predicate2 = new TutorialTagContainsGroupPredicate("THU10");
+ AvailableCommand command1 = new AvailableCommand(predicate1, WED10);
+ AvailableCommand command2 = new AvailableCommand(predicate2, THU10);
+
+ // same object -> returns true
+ assertTrue(command1.equals(command1));
+
+ // same values -> returns true
+ AvailableCommand command1Copy = new AvailableCommand(predicate1, WED10);
+ assertTrue(command1.equals(command1Copy));
+
+ // different types -> returns false
+ assertFalse(command1.equals(1));
+
+ // null -> returns false
+ assertFalse(command1.equals(null));
+
+ // different command -> returns false
+ assertFalse(command1.equals(command2));
+ }
+
+
+ @Test
+ public void execute_invalidTutorialTag_throwsCommandException() {
+ Model model = new ModelManager();
+ TutorialTagContainsGroupPredicate predicate = new TutorialTagContainsGroupPredicate("TUES08");
+ AvailableCommand command = new AvailableCommand(predicate, new TutorialTag("TUES08", TagStatus.ASSIGNED));
+
+ try {
+ command.execute(model);
+ } catch (CommandException e) {
+ assertEquals("Specified tutorial tag name is not allowed: TUES08", e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
index 80d9110c03a..2a3cadb08c4 100644
--- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java
@@ -1,5 +1,6 @@
package seedu.address.logic.commands;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
@@ -12,6 +13,14 @@
public class ClearCommandTest {
+ @Test
+ public void execute_getNeedsWarningPopup_returnsTrue() {
+
+ ClearCommand clearCommand = new ClearCommand();
+
+ assertTrue(clearCommand.getNeedsWarningPopup());
+ }
+
@Test
public void execute_emptyAddressBook_success() {
Model model = new ModelManager();
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index 643a1d08069..059e7fc5fc5 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -2,22 +2,27 @@
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_ID;
import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_TAGSTATUS;
+import static seedu.address.logic.parser.CliSyntax.TYPE_STU;
+import static seedu.address.logic.parser.CliSyntax.TYPE_TA;
import static seedu.address.testutil.Assert.assertThrows;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
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.FieldContainsKeywordsPredicate;
import seedu.address.model.person.Person;
import seedu.address.testutil.EditPersonDescriptorBuilder;
@@ -26,33 +31,88 @@
*/
public class CommandTestUtil {
+ public static final String VALID_TYPE_AMY = "stu";
+ public static final String VALID_TYPE_BOB = "ta";
public static final String VALID_NAME_AMY = "Amy Bee";
public static final String VALID_NAME_BOB = "Bob Choo";
+
+ public static final String VALID_NAME_CARL = "Carl Kurz";
+ public static final String VALID_ID_AMY = "A1234567Z";
+ public static final String VALID_ID_BOB = "A0123456B";
+
+ public static final String VALID_ID_CARL = "A2222222D";
public static final String VALID_PHONE_AMY = "11111111";
public static final String VALID_PHONE_BOB = "22222222";
+
+ public static final String VALID_PHONE_CARL = "95352563";
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_EMAIL_CARL = "heinz@example.com";
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 VALID_TAG_FRIENDS = "friends";
+ public static final String VALID_TAG_ASSIGNMENT1 = "Assignment1";
+ public static final String VALID_TAG_ASSIGNMENT2 = "Assignment2";
+ public static final String VALID_TAG_ASSIGNMENT3 = "Assignment3";
+
+ public static final String VALID_TAGSTATUS_COMPLETE_GOOD = "cg";
+ public static final String VALID_TAGSTATUS_COMPLETE_BAD = "cb";
+ public static final String INVALID_EDIT_TUTORIAL_TAG_FLAG = "out";
+ public static final String VALID_EDIT_TUTORIAL_TAG_FLAG = "add";
+ public static final String TYPE_DESC_AMY = " " + TYPE_STU + " ";
+ public static final String TYPE_DESC_BOB = " " + TYPE_TA + " ";
+ public static final String TYPE_DESC_CARL = " " + TYPE_STU + " ";
+ 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 NAME_DESC_CARL = " " + PREFIX_NAME + " " + VALID_NAME_CARL;
+ public static final String ID_DESC_AMY = " " + PREFIX_ID + " " + VALID_ID_AMY;
+ public static final String ID_DESC_BOB = " " + PREFIX_ID + " " + VALID_ID_BOB;
+ public static final String ID_DESC_CARL = " " + PREFIX_ID + " " + VALID_ID_CARL;
+ 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 PHONE_DESC_CARL = " " + PREFIX_PHONE + " " + VALID_PHONE_CARL;
+ 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 EMAIL_DESC_CARL = " " + PREFIX_EMAIL + " " + VALID_EMAIL_CARL;
+ public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + " " + VALID_TAG_FRIENDS;
+ public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + " " + VALID_TAG_HUSBAND;
+ public static final String TAG_DESC_ASS1_ASS2 =
+ " " + PREFIX_TAG + " " + VALID_TAG_ASSIGNMENT1 + " " + VALID_TAG_ASSIGNMENT2;
+ public static final String TAG_DESC_ASS3 = " " + PREFIX_TAG + " " + VALID_TAG_ASSIGNMENT3;
+ public static final String TAG_DESC_ASS1_MULTIPLE_WHITESPACES_ASS2 =
+ " " + PREFIX_TAG + " " + VALID_TAG_ASSIGNMENT1 + " " + VALID_TAG_ASSIGNMENT2;
+ public static final String TAGSTATUS_DESC_COMPLETE_GOOD =
+ " " + PREFIX_TAGSTATUS + " " + VALID_TAGSTATUS_COMPLETE_GOOD;
+ public static final String TAGSTATUS_DESC_COMPLETE_BAD =
+ " " + PREFIX_TAGSTATUS + " " + VALID_TAGSTATUS_COMPLETE_BAD;
+ public static final String TAG_FRIEND_TAGSTATUS_COMPLETE_GOOD = " " + PREFIX_TAG + " " + VALID_TAG_FRIENDS
+ + TAGSTATUS_DESC_COMPLETE_GOOD;
+ public static final String TAG_FRIEND_TAGSTATUS_COMPLETE_BAD = " " + PREFIX_TAG + " " + VALID_TAG_FRIENDS
+ + TAGSTATUS_DESC_COMPLETE_BAD;
+ public static final String TAG_ASS1_ASS2_TAGSTATUS_COMPLETE_GOOD =
+ TAG_DESC_ASS1_ASS2 + TAGSTATUS_DESC_COMPLETE_GOOD;
+ public static final String TAG_ASS3_TAGSTATUS_COMPLETE_BAD = TAG_DESC_ASS3 + TAGSTATUS_DESC_COMPLETE_BAD;
+ public static final String TAG_ASS1_MULTIPLE_WHITESPACES_ASS2_TAGSTATUS_COMPLETE_GOOD =
+ TAG_DESC_ASS1_MULTIPLE_WHITESPACES_ASS2 + TAGSTATUS_DESC_COMPLETE_GOOD;
+ public static final Set VALID_TAGNAMES_SET_FRIENDS = new HashSet<>(List.of(VALID_TAG_FRIENDS));
+ public static final Set VALID_TAGNAMES_SET_ASS1_ASS2 = new HashSet<>(List.of(VALID_TAG_ASSIGNMENT1,
+ VALID_TAG_ASSIGNMENT2));
+ public static final Set VALID_TAGNAMES_SET_ASS3 = new HashSet<>(List.of(
+ VALID_TAG_ASSIGNMENT3));
+
+ public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + " " + "James&"; // '&' not allowed in names
+ public static final String INVALID_ID_DESC = " " + PREFIX_ID + " " + "123456789";
+ // IDs must start and end with a letter
+ 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_TAG_DESC = " " + PREFIX_TAG + " " + "ass*"; // '*' not allowed in tags
+ public static final String VALID_TAG_FRIEND_DESC = " " + PREFIX_TAG + " "
+ + VALID_TAG_FRIENDS; // '*' not allowed in tags
+
+ public static final String INVALID_TAG_TAGSTATUS_COMPLETE_GOOD = INVALID_TAG_DESC + " " + PREFIX_TAGSTATUS
+ + " " + VALID_TAGSTATUS_COMPLETE_GOOD;
+ public static final String VALID_TAG_FRIEND_TAGSTATUS_COMPLETE_GOOD = VALID_TAG_FRIEND_DESC + " "
+ + PREFIX_TAGSTATUS + " " + VALID_TAGSTATUS_COMPLETE_GOOD;
public static final String PREAMBLE_WHITESPACE = "\t \r \n";
public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble";
@@ -62,11 +122,9 @@ public class CommandTestUtil {
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();
+ .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).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();
+ .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).build();
}
/**
@@ -120,7 +178,7 @@ public static void showPersonAtIndex(Model model, Index targetIndex) {
Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased());
final String[] splitName = person.getName().fullName.split("\\s+");
- model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0])));
+ model.updateFilteredPersonList(new FieldContainsKeywordsPredicate(PREFIX_NAME, Arrays.asList(splitName[0])));
assertEquals(1, model.getFilteredPersonList().size());
}
diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
index b6f332eabca..0b995db56cc 100644
--- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
@@ -10,6 +10,10 @@
import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
import org.junit.jupiter.api.Test;
import seedu.address.commons.core.index.Index;
@@ -28,9 +32,21 @@ public class DeleteCommandTest {
private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
@Test
- public void execute_validIndexUnfilteredList_success() {
+ public void execute_getNeedsWarningPopup_returnsTrue() {
+
+ Index[] indexArr = {INDEX_FIRST_PERSON};
+ Set indexSet = new HashSet<>(Arrays.asList(indexArr));
+ DeleteCommand deleteCommand = new DeleteCommand(indexSet);
+
+ assertTrue(deleteCommand.getNeedsWarningPopup());
+ }
+
+ @Test
+ public void execute_validSingleIndexUnfilteredList_success() {
Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
- DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON);
+ Index[] indexArr = {INDEX_FIRST_PERSON};
+ Set indexSet = new HashSet<>(Arrays.asList(indexArr));
+ DeleteCommand deleteCommand = new DeleteCommand(indexSet);
String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
Messages.format(personToDelete));
@@ -42,19 +58,44 @@ public void execute_validIndexUnfilteredList_success() {
}
@Test
- public void execute_invalidIndexUnfilteredList_throwsCommandException() {
+ public void execute_validMultipleIndexUnfilteredList_success() {
+ Person personToDelete1 = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person personToDelete2 = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased());
+ Index[] indexArr = {INDEX_FIRST_PERSON, INDEX_SECOND_PERSON};
+ Set indexSet = new HashSet<>(Arrays.asList(indexArr));
+ DeleteCommand deleteCommand = new DeleteCommand(indexSet);
+
+ String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
+ Messages.format(personToDelete1)) + "\n" + String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
+ Messages.format(personToDelete2));
+
+ ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.deletePerson(personToDelete1);
+ expectedModel.deletePerson(personToDelete2);
+
+ assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidSingleIndexUnfilteredList_throwsCommandException() {
Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
- DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex);
+ Index[] indexArr = {outOfBoundIndex};
+ Set indexSet = new HashSet<>(Arrays.asList(indexArr));
+ DeleteCommand deleteCommand = new DeleteCommand(indexSet);
- assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ assertCommandFailure(deleteCommand, model,
+ String.format(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX,
+ "[" + outOfBoundIndex.getOneBased() + "]"));
}
@Test
- public void execute_validIndexFilteredList_success() {
+ public void execute_validSingleIndexFilteredList_success() {
showPersonAtIndex(model, INDEX_FIRST_PERSON);
Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
- DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON);
+ Index[] indexArr = {INDEX_FIRST_PERSON};
+ Set indexSet = new HashSet<>(Arrays.asList(indexArr));
+ DeleteCommand deleteCommand = new DeleteCommand(indexSet);
String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
Messages.format(personToDelete));
@@ -67,28 +108,38 @@ public void execute_validIndexFilteredList_success() {
}
@Test
- public void execute_invalidIndexFilteredList_throwsCommandException() {
+ public void execute_invalidSingleIndexFilteredList_throwsCommandException() {
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());
- DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex);
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
+ Index[] indexArr = {outOfBoundIndex};
+ Set indexSet = new HashSet<>(Arrays.asList(indexArr));
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ DeleteCommand deleteCommand = new DeleteCommand(indexSet);
- assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ assertCommandFailure(deleteCommand, model,
+ String.format(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX,
+ "[" + INDEX_SECOND_PERSON.getOneBased() + "]"));
}
@Test
public void equals() {
- DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_PERSON);
- DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_PERSON);
+ Index[] firstPersonIndexArr = {INDEX_FIRST_PERSON};
+ Set firstPersonIndexSet = new HashSet<>(Arrays.asList(firstPersonIndexArr));
+ Index[] secondPersonIndexArr = {INDEX_SECOND_PERSON};
+ Set secondPersonIndexSet = new HashSet<>(Arrays.asList(secondPersonIndexArr));
+
+
+ DeleteCommand deleteFirstCommand = new DeleteCommand(firstPersonIndexSet);
+ DeleteCommand deleteSecondCommand = new DeleteCommand(secondPersonIndexSet);
// same object -> returns true
assertTrue(deleteFirstCommand.equals(deleteFirstCommand));
// same values -> returns true
- DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON);
+ DeleteCommand deleteFirstCommandCopy = new DeleteCommand(firstPersonIndexSet);
assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy));
// different types -> returns false
@@ -104,8 +155,10 @@ public void equals() {
@Test
public void toStringMethod() {
Index targetIndex = Index.fromOneBased(1);
- DeleteCommand deleteCommand = new DeleteCommand(targetIndex);
- String expected = DeleteCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ Index[] indexArr = {targetIndex};
+ Set indexSet = new HashSet<>(Arrays.asList(indexArr));
+ DeleteCommand deleteCommand = new DeleteCommand(indexSet);
+ String expected = DeleteCommand.class.getCanonicalName() + "{index=[" + targetIndex + "]}";
assertEquals(expected, deleteCommand.toString());
}
diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
index 469dd97daa7..5f744006069 100644
--- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
@@ -7,7 +7,6 @@
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;
@@ -37,7 +36,7 @@ public class EditCommandTest {
@Test
public void execute_allFieldsSpecifiedUnfilteredList_success() {
- Person editedPerson = new PersonBuilder().build();
+ Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build();
EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor);
@@ -55,11 +54,10 @@ public void execute_someFieldsSpecifiedUnfilteredList_success() {
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();
+ Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB).build();
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB)
- .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build();
+ .withPhone(VALID_PHONE_BOB).build();
EditCommand editCommand = new EditCommand(indexLastPerson, descriptor);
String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson));
@@ -99,27 +97,6 @@ public void execute_filteredList_success() {
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);
diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
index b17c1f3d5c2..561a19d267c 100644
--- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
@@ -5,11 +5,9 @@
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;
@@ -47,14 +45,6 @@ public void equals() {
// 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
@@ -63,9 +53,7 @@ public void toStringMethod() {
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) + "}";
+ + editPersonDescriptor.getEmail().orElse(null) + "}";
assertEquals(expected, editPersonDescriptor.toString());
}
}
diff --git a/src/test/java/seedu/address/logic/commands/EditTutTagListCommandTest.java b/src/test/java/seedu/address/logic/commands/EditTutTagListCommandTest.java
new file mode 100644
index 00000000000..d4f150d1eac
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/EditTutTagListCommandTest.java
@@ -0,0 +1,263 @@
+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.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.TypicalTutorialTag.WED10;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+
+import org.junit.jupiter.api.Test;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.commons.core.GuiSettings;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.person.Person;
+import seedu.address.model.tag.TutorialTag;
+
+class EditTutTagListCommandTest {
+ static final String TAGNAME_WED10 = "WED10";
+ static final String TAGNAME_THU10 = "THU10";
+
+ @Test
+ public void execute_tutorialTagAcceptedByModel_addSuccessful() throws Exception {
+ EditTutTagListCommandTest.ModelStubAcceptingTutorialTagAdded modelStub =
+ new EditTutTagListCommandTest.ModelStubAcceptingTutorialTagAdded();
+
+ new EditTutTagListCommand(TAGNAME_WED10, EditTutTagListCommand.CommandSubtype.ADD)
+ .execute(modelStub);
+
+ assertEquals(Arrays.asList(WED10), modelStub.tutorialTagAdded);
+ }
+
+ @Test
+ public void execute_tutorialTagDeletedByModel_addSuccessful() throws Exception {
+ EditTutTagListCommandTest.ModelStubAcceptingTutorialTagAdded modelStub =
+ new EditTutTagListCommandTest.ModelStubAcceptingTutorialTagAdded();
+
+ modelStub.addTutorialTag(WED10);
+
+ new EditTutTagListCommand(TAGNAME_WED10, EditTutTagListCommand.CommandSubtype.DELETE)
+ .execute(modelStub);
+
+ assertFalse(modelStub.hasTutorialTag(WED10));
+ }
+
+ @Test
+ public void execute_tutorialAddDuplicateTutorialTag_addFailed() throws Exception {
+ EditTutTagListCommandTest.ModelStubAcceptingTutorialTagAdded modelStub =
+ new EditTutTagListCommandTest.ModelStubAcceptingTutorialTagAdded();
+
+ modelStub.addTutorialTag(WED10);
+
+ EditTutTagListCommand command = new EditTutTagListCommand(TAGNAME_WED10,
+ EditTutTagListCommand.CommandSubtype.ADD);
+
+ assertThrows(CommandException.class, () -> command.execute(modelStub));
+ }
+
+ @Test
+ public void execute_listIsFiltered_showsEverything() throws Exception {
+ EditTutTagListCommandTest.ModelStubAcceptingTutorialTagAdded modelStub =
+ new EditTutTagListCommandTest.ModelStubAcceptingTutorialTagAdded();
+ EditTutTagListCommand command = new EditTutTagListCommand(EditTutTagListCommand.CommandSubtype.LIST);
+ CommandResult result = command.execute(modelStub);
+ CommandResult expectedResult = new CommandResult(EditTutTagListCommand.EMPTY_TUTORIALTAGLIST_OUTPUT);
+ assertEquals(expectedResult, result);
+ }
+
+
+ @Test
+ public void equals() {
+ EditTutTagListCommand addWed10Command = new EditTutTagListCommand(TAGNAME_WED10,
+ EditTutTagListCommand.CommandSubtype.ADD);
+ EditTutTagListCommand addThu10Command = new EditTutTagListCommand(TAGNAME_THU10,
+ EditTutTagListCommand.CommandSubtype.ADD);
+
+ // same object -> returns true
+ assertTrue(addWed10Command.equals(addWed10Command));
+
+ // same values -> returns true
+ EditTutTagListCommand addWed10CommandCopy = new EditTutTagListCommand(TAGNAME_WED10,
+ EditTutTagListCommand.CommandSubtype.ADD);
+ assertTrue(addWed10Command.equals(addWed10CommandCopy));
+
+ // different types -> returns false
+ assertFalse(addWed10Command.equals(1));
+
+ // null -> returns false
+ assertFalse(addWed10Command.equals(null));
+
+ // different tutorialTag -> returns false
+ assertFalse(addWed10Command.equals(addThu10Command));
+ }
+
+ @Test
+ public void toStringMethod() {
+ EditTutTagListCommand editTutTagListCommand = new EditTutTagListCommand(TAGNAME_WED10,
+ EditTutTagListCommand.CommandSubtype.ADD);
+ String expected = EditTutTagListCommand.class.getCanonicalName() + "{tagName=" + TAGNAME_WED10 + "}";
+ assertEquals(expected, editTutTagListCommand.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 getPersonList() {
+ 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.");
+ }
+
+ @Override
+ public void persistentUpdateFilteredList(List extends Predicate> predicates) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void deleteTutorialTag(TutorialTag target) {
+ throw new AssertionError("This method should not be called.");
+ };
+ @Override
+ public void addTutorialTag(TutorialTag tutorialTag) {
+ throw new AssertionError("This method should not be called.");
+ };
+ @Override
+ public boolean hasTutorialTag(TutorialTag tutorialTag) {
+ throw new AssertionError("This method should not be called.");
+ };
+ @Override
+ public ObservableList getTutorialTagList() {
+ throw new AssertionError("This method should not be called.");
+ };
+
+ @Override
+ public String getTutorialTagListString() {
+ throw new AssertionError("This method should not be called.");
+ }
+ }
+
+ /**
+ * A Model stub that always accept the person being added.
+ */
+ private class ModelStubAcceptingTutorialTagAdded extends ModelStub {
+ final ArrayList tutorialTagAdded = new ArrayList<>();
+
+ @Override
+ public boolean hasTutorialTag(TutorialTag tutorialTag) {
+ requireNonNull(tutorialTag);
+ return tutorialTagAdded.stream().anyMatch(tutorialTag::isSameTutorialTag);
+ }
+
+ @Override
+ public void addTutorialTag(TutorialTag tutorialTag) {
+ requireNonNull(tutorialTag);
+ tutorialTagAdded.add(tutorialTag);
+ }
+
+ @Override
+ public ReadOnlyAddressBook getAddressBook() {
+ return new AddressBook();
+ }
+
+ @Override
+ public ObservableList getTutorialTagList() {
+ return FXCollections.observableArrayList(tutorialTagAdded);
+ };
+
+ @Override
+ public void deleteTutorialTag(TutorialTag target) {
+ tutorialTagAdded.remove(target);
+ };
+
+ @Override
+ public ObservableList getPersonList() {
+ return new AddressBook().getPersonList();
+ }
+
+ public String getTutorialTagListString() {
+ return "Available Tutorial Tag(s): [ ]";
+ }
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
index b8b7dbba91a..eee493e87d4 100644
--- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/FindCommandTest.java
@@ -4,7 +4,9 @@
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.Messages.MESSAGE_PERSON_LISTED_OVERVIEW;
import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.testutil.TypicalPersons.CARL;
import static seedu.address.testutil.TypicalPersons.ELLE;
import static seedu.address.testutil.TypicalPersons.FIONA;
@@ -18,7 +20,7 @@
import seedu.address.model.Model;
import seedu.address.model.ModelManager;
import seedu.address.model.UserPrefs;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.person.FieldContainsKeywordsPredicate;
/**
* Contains integration tests (interaction with the Model) for {@code FindCommand}.
@@ -29,19 +31,19 @@ public class FindCommandTest {
@Test
public void equals() {
- NameContainsKeywordsPredicate firstPredicate =
- new NameContainsKeywordsPredicate(Collections.singletonList("first"));
- NameContainsKeywordsPredicate secondPredicate =
- new NameContainsKeywordsPredicate(Collections.singletonList("second"));
+ FieldContainsKeywordsPredicate[] firstPredicate =
+ {new FieldContainsKeywordsPredicate(Collections.singletonList("first"))};
+ FieldContainsKeywordsPredicate[] secondPredicate =
+ {new FieldContainsKeywordsPredicate(Collections.singletonList("second"))};
- FindCommand findFirstCommand = new FindCommand(firstPredicate);
- FindCommand findSecondCommand = new FindCommand(secondPredicate);
+ FindCommand findFirstCommand = new FindCommand(Arrays.asList(firstPredicate));
+ FindCommand findSecondCommand = new FindCommand(Arrays.asList(secondPredicate));
// same object -> returns true
assertTrue(findFirstCommand.equals(findFirstCommand));
// same values -> returns true
- FindCommand findFirstCommandCopy = new FindCommand(firstPredicate);
+ FindCommand findFirstCommandCopy = new FindCommand(Arrays.asList(firstPredicate));
assertTrue(findFirstCommand.equals(findFirstCommandCopy));
// different types -> returns false
@@ -56,10 +58,10 @@ public void equals() {
@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);
+ String expectedMessage = String.format(MESSAGE_PERSON_LISTED_OVERVIEW, 0);
+ FieldContainsKeywordsPredicate[] predicate = {preparePredicate(" ")};
+ FindCommand command = new FindCommand(Arrays.asList(predicate));
+ expectedModel.updateFilteredPersonList(predicate[0]);
assertCommandSuccess(command, model, expectedMessage, expectedModel);
assertEquals(Collections.emptyList(), model.getFilteredPersonList());
}
@@ -67,25 +69,26 @@ public void execute_zeroKeywords_noPersonFound() {
@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);
+ FieldContainsKeywordsPredicate[] predicate = {preparePredicate("Kurz Elle Kunz")};
+ FindCommand command = new FindCommand(Arrays.asList(predicate));
+ expectedModel.updateFilteredPersonList(predicate[0]);
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 + "}";
+ FieldContainsKeywordsPredicate[] predicate = {new FieldContainsKeywordsPredicate(Arrays.asList("keyword"))};
+ FindCommand findCommand = new FindCommand(Arrays.asList(predicate));
+ String expected = FindCommand.class.getCanonicalName() + "{predicate=" + predicate[0] + "}";
+
assertEquals(expected, findCommand.toString());
}
/**
* Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}.
*/
- private NameContainsKeywordsPredicate preparePredicate(String userInput) {
- return new NameContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+")));
+ private FieldContainsKeywordsPredicate preparePredicate(String userInput) {
+ return new FieldContainsKeywordsPredicate(PREFIX_NAME, Arrays.asList(userInput.split("\\s+")));
}
}
diff --git a/src/test/java/seedu/address/logic/commands/MarkCommandTest.java b/src/test/java/seedu/address/logic/commands/MarkCommandTest.java
new file mode 100644
index 00000000000..b15d9a9ce51
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/MarkCommandTest.java
@@ -0,0 +1,145 @@
+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.VALID_TAGNAMES_SET_ASS1_ASS2;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAGNAMES_SET_ASS3;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_ASSIGNMENT3;
+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_1_SET;
+import static seedu.address.testutil.TypicalIndexes.INDEX_2_SET;
+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 java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+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.model.tag.TagStatus;
+import seedu.address.testutil.PersonBuilder;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for RemarkCommand.
+ */
+public class MarkCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_filteredList_success() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+
+ PersonBuilder updatedPersonBuilder = new PersonBuilder(personInFilteredList);
+ VALID_TAGNAMES_SET_ASS1_ASS2.forEach(x -> updatedPersonBuilder.addTag(x, TagStatus.COMPLETE_GOOD));
+ Person updatedPerson = updatedPersonBuilder.build();
+
+ MarkCommand markCommand = new MarkCommand(INDEX_1_SET, VALID_TAGNAMES_SET_ASS1_ASS2,
+ TagStatus.COMPLETE_GOOD);
+
+ String expectedMessage = String.format(MarkCommand.MESSAGE_MARK_PERSON_SUCCESS, Messages.format(updatedPerson));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), updatedPerson);
+
+ assertCommandSuccess(markCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidPersonIndexUnfilteredList_failure() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ Set outOfBoundIndexSet = new HashSet<>(List.of(outOfBoundIndex));
+ MarkCommand markCommand = new MarkCommand(outOfBoundIndexSet, VALID_TAGNAMES_SET_ASS1_ASS2,
+ TagStatus.COMPLETE_GOOD);
+
+ assertCommandFailure(markCommand, model, String.format(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX,
+ model.getFilteredPersonList().size() + 1));
+ }
+
+ /**
+ * 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;
+ Set outOfBoundIndexSet = new HashSet<>(List.of(outOfBoundIndex));
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
+
+ MarkCommand markCommand = new MarkCommand(outOfBoundIndexSet, VALID_TAGNAMES_SET_ASS1_ASS2,
+ TagStatus.COMPLETE_GOOD);
+
+ assertCommandFailure(markCommand, model, String.format(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX,
+ outOfBoundIndex.getOneBased()));
+ }
+
+ @Test
+ public void equals() {
+ final MarkCommand standardCommand = new MarkCommand(INDEX_1_SET, VALID_TAGNAMES_SET_ASS1_ASS2,
+ TagStatus.DEFAULT_STATUS);
+
+ // same values -> returns true
+ MarkCommand commandWithSameValues = new MarkCommand(INDEX_1_SET, VALID_TAGNAMES_SET_ASS1_ASS2,
+ TagStatus.DEFAULT_STATUS);
+ 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 MarkCommand(INDEX_2_SET, VALID_TAGNAMES_SET_ASS1_ASS2,
+ TagStatus.DEFAULT_STATUS)));
+
+ // different tagName -> returns false
+ assertFalse(standardCommand.equals(new MarkCommand(INDEX_1_SET,
+ VALID_TAGNAMES_SET_ASS3, TagStatus.DEFAULT_STATUS)));
+
+ // different tagStatus -> returns false
+ assertFalse(standardCommand.equals(new MarkCommand(INDEX_1_SET, VALID_TAGNAMES_SET_ASS1_ASS2,
+ TagStatus.COMPLETE_GOOD)));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index index = Index.fromOneBased(1);
+ Set indexSet = new HashSet<>(List.of(index));
+ MarkCommand markCommand = new MarkCommand(indexSet, VALID_TAGNAMES_SET_ASS1_ASS2, TagStatus.COMPLETE_GOOD);
+ String expected = MarkCommand.class.getCanonicalName() + "{index=" + indexSet + ", tagName(s)="
+ + VALID_TAGNAMES_SET_ASS1_ASS2 + ", tagStatus=" + TagStatus.COMPLETE_GOOD + "}";
+ assertEquals(expected, markCommand.toString());
+ }
+
+ @Test
+ public void execute_nonExistingTutorialTagMarking_failure() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ MarkCommand markCommand = new MarkCommand(INDEX_1_SET, VALID_TAGNAMES_SET_ASS3,
+ TagStatus.AVAILABLE);
+
+ String expectedMessage = Messages.MESSAGE_INVALID_TUTORIAL_TAG_VALUE + VALID_TAG_ASSIGNMENT3;
+ assertCommandFailure(markCommand, model, expectedMessage);
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/RemovetagCommandTest.java b/src/test/java/seedu/address/logic/commands/RemovetagCommandTest.java
new file mode 100644
index 00000000000..dc1bdf5e747
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/RemovetagCommandTest.java
@@ -0,0 +1,117 @@
+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.VALID_TAGNAMES_SET_FRIENDS;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIENDS;
+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_1_SET;
+import static seedu.address.testutil.TypicalIndexes.INDEX_2_SET;
+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 java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+import seedu.address.testutil.PersonBuilder;
+
+class RemovetagCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ void execute() {
+ }
+
+ @Test
+ public void execute_filteredList_success() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+
+ Person updatedPerson = new PersonBuilder(personInFilteredList).removeTag(VALID_TAG_FRIENDS).build();
+ RemovetagCommand removetagCommand = new RemovetagCommand(INDEX_1_SET, VALID_TAGNAMES_SET_FRIENDS);
+
+ String expectedMessage = String.format(RemovetagCommand.MESSAG_REMOVETAG_SUCCESS,
+ Messages.format(updatedPerson));
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(model.getFilteredPersonList().get(0), updatedPerson);
+
+ assertCommandSuccess(removetagCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidPersonIndexUnfilteredList_failure() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ Set outOfBoundIndexSet = new HashSet<>(List.of(outOfBoundIndex));
+ RemovetagCommand removetagCommand = new RemovetagCommand(outOfBoundIndexSet, VALID_TAGNAMES_SET_FRIENDS);
+
+ assertCommandFailure(removetagCommand, 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;
+ Set outOfBoundIndexSet = new HashSet<>(List.of(outOfBoundIndex));
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
+
+ RemovetagCommand removetagCommand = new RemovetagCommand(outOfBoundIndexSet, VALID_TAGNAMES_SET_FRIENDS);
+
+ assertCommandFailure(removetagCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ public void equals() {
+ final RemovetagCommand standardCommand = new RemovetagCommand(INDEX_1_SET, VALID_TAGNAMES_SET_FRIENDS);
+
+ // same values -> returns true
+ RemovetagCommand commandWithSameValues = new RemovetagCommand(INDEX_1_SET, VALID_TAGNAMES_SET_FRIENDS);
+ 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 RemovetagCommand(INDEX_2_SET, VALID_TAGNAMES_SET_FRIENDS)));
+
+ // different tagName -> returns false
+ assertFalse(standardCommand.equals(new RemovetagCommand(INDEX_2_SET, VALID_TAGNAMES_SET_FRIENDS)));
+
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index index = Index.fromOneBased(1);
+ Set indexSet = new HashSet<>(List.of(index));
+ RemovetagCommand removetagCommand = new RemovetagCommand(indexSet, VALID_TAGNAMES_SET_FRIENDS);
+ String expected = RemovetagCommand.class.getCanonicalName() + "{index=" + indexSet + ", tagName(s)="
+ + VALID_TAGNAMES_SET_FRIENDS + "}";
+ assertEquals(expected, removetagCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
index 5bc11d3cdaa..16cb2560647 100644
--- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
@@ -1,48 +1,49 @@
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.Messages.MESSAGE_INVALID_PERSON_TYPE;
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.EMAIL_DESC_CARL;
+import static seedu.address.logic.commands.CommandTestUtil.ID_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.ID_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.ID_DESC_CARL;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_ID_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.NAME_DESC_CARL;
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.PHONE_DESC_CARL;
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.TYPE_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.TYPE_DESC_CARL;
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_ID;
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 static seedu.address.testutil.TypicalPersons.CARL;
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.Id;
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 {
@@ -50,30 +51,26 @@ public class AddCommandParserTest {
@Test
public void parse_allFieldsPresent_success() {
- Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build();
+ Person expectedPerson = new PersonBuilder(CARL).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));
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + TYPE_DESC_CARL + NAME_DESC_CARL + ID_DESC_CARL
+ + PHONE_DESC_CARL + EMAIL_DESC_CARL,
+ new AddCommand(expectedPerson));
}
@Test
public void parse_repeatedNonTagValue_failure() {
- String validExpectedPersonString = NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_FRIEND;
+ String validExpectedPersonString = NAME_DESC_BOB + ID_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB;
// multiple names
assertParseFailure(parser, NAME_DESC_AMY + validExpectedPersonString,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+ // multiple id
+ assertParseFailure(parser, ID_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ID));
+
// multiple phones
assertParseFailure(parser, PHONE_DESC_AMY + validExpectedPersonString,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
@@ -82,15 +79,12 @@ public void parse_repeatedNonTagValue_failure() {
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 + ID_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + NAME_DESC_AMY
+ validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ADDRESS, PREFIX_EMAIL, PREFIX_PHONE));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ID, PREFIX_EMAIL,
+ PREFIX_PHONE));
// invalid value followed by valid value
@@ -98,6 +92,10 @@ public void parse_repeatedNonTagValue_failure() {
assertParseFailure(parser, INVALID_NAME_DESC + validExpectedPersonString,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
+ // invalid id
+ assertParseFailure(parser, INVALID_ID_DESC + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ID));
+
// invalid email
assertParseFailure(parser, INVALID_EMAIL_DESC + validExpectedPersonString,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
@@ -106,10 +104,6 @@ public void parse_repeatedNonTagValue_failure() {
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
@@ -123,17 +117,13 @@ public void parse_repeatedNonTagValue_failure() {
// 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,
+ assertParseSuccess(parser, NAME_DESC_AMY + ID_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY,
new AddCommand(expectedPerson));
}
@@ -142,55 +132,51 @@ 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,
+ assertParseFailure(parser, VALID_NAME_BOB + ID_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB,
expectedMessage);
// missing phone prefix
- assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser, NAME_DESC_BOB + ID_DESC_BOB + VALID_PHONE_BOB + EMAIL_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,
+ assertParseFailure(parser, NAME_DESC_BOB + ID_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB,
expectedMessage);
// all prefixes missing
- assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB,
+ assertParseFailure(parser, VALID_NAME_BOB + ID_DESC_BOB + VALID_PHONE_BOB + VALID_EMAIL_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);
+ assertParseFailure(parser,
+ TYPE_DESC_BOB + INVALID_NAME_DESC + ID_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB,
+ Name.MESSAGE_CONSTRAINTS);
+
+ // invalid id
+ assertParseFailure(parser,
+ TYPE_DESC_BOB + NAME_DESC_BOB + INVALID_ID_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB,
+ Id.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);
+ assertParseFailure(parser,
+ TYPE_DESC_BOB + NAME_DESC_BOB + ID_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB,
+ 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);
+ assertParseFailure(parser,
+ TYPE_DESC_BOB + NAME_DESC_BOB + ID_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC,
+ Email.MESSAGE_CONSTRAINTS);
// two invalid values, only first invalid value reported
- assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC,
+ assertParseFailure(parser,
+ TYPE_DESC_BOB + INVALID_NAME_DESC + PHONE_DESC_BOB + ID_DESC_BOB + EMAIL_DESC_BOB,
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));
+ // invalid person type
+ assertParseFailure(parser, PREAMBLE_NON_EMPTY + TYPE_DESC_BOB + NAME_DESC_BOB + ID_DESC_BOB
+ + PHONE_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND, MESSAGE_INVALID_PERSON_TYPE);
}
}
diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
index 5a1ab3dbc0c..a31839a44e3 100644
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
@@ -4,26 +4,34 @@
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.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
+import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AvailableCommand;
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.EditTutTagListCommand;
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.commands.MarkCommand;
+import seedu.address.logic.commands.RemovetagCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.person.FieldContainsKeywordsPredicate;
import seedu.address.model.person.Person;
import seedu.address.testutil.EditPersonDescriptorBuilder;
import seedu.address.testutil.PersonBuilder;
@@ -50,7 +58,9 @@ public void parseCommand_clear() throws Exception {
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);
+ Index[] indexArr = {INDEX_FIRST_PERSON};
+ Set indexSet = new HashSet<>(Arrays.asList(indexArr));
+ assertEquals(new DeleteCommand(indexSet), command);
}
@Test
@@ -72,8 +82,10 @@ public void parseCommand_exit() throws Exception {
public void parseCommand_find() throws Exception {
List keywords = Arrays.asList("foo", "bar", "baz");
FindCommand command = (FindCommand) parser.parseCommand(
- FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" ")));
- assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command);
+ FindCommand.COMMAND_WORD + " " + PREFIX_NAME + " "
+ + keywords.stream().collect(Collectors.joining(" ")));
+ FieldContainsKeywordsPredicate[] predicateList = {new FieldContainsKeywordsPredicate(PREFIX_NAME, keywords)};
+ assertEquals(new FindCommand(Arrays.asList(predicateList)), command);
}
@Test
@@ -88,6 +100,26 @@ public void parseCommand_list() throws Exception {
assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand);
}
+ @Test
+ public void parseCommand_mark() throws Exception {
+ assertTrue(parser.parseCommand(MarkCommand.SAMPLE_COMMAND) instanceof MarkCommand);
+ }
+
+ @Test
+ public void parseCommand_removetag() throws Exception {
+ assertTrue(parser.parseCommand(RemovetagCommand.SAMPLE_COMMAND) instanceof RemovetagCommand);
+ }
+
+ @Test
+ public void parseCommand_available() throws Exception {
+ assertTrue(parser.parseCommand(AvailableCommand.SAMPLE_COMMAND) instanceof AvailableCommand);
+ }
+
+ @Test
+ public void parseCommand_editTutTagList() throws Exception {
+ assertTrue(parser.parseCommand(EditTutTagListCommand.SAMPLE_COMMAND) instanceof EditTutTagListCommand);
+ }
+
@Test
public void parseCommand_unrecognisedInput_throwsParseException() {
assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), ()
diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
index c97308935f5..d5fe97178de 100644
--- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
+++ b/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java
@@ -10,17 +10,17 @@
public class ArgumentTokenizerTest {
private final Prefix unknownPrefix = new Prefix("--u");
- private final Prefix pSlash = new Prefix("p/");
+ private final Prefix slashP = new Prefix("/p");
private final Prefix dashT = new Prefix("-t");
private final Prefix hatQ = new Prefix("^Q");
@Test
public void tokenize_emptyArgsString_noValues() {
String argsString = " ";
- ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, slashP);
assertPreambleEmpty(argMultimap);
- assertArgumentAbsent(argMultimap, pSlash);
+ assertArgumentAbsent(argMultimap, slashP);
}
private void assertPreamblePresent(ArgumentMultimap argMultimap, String expectedPreamble) {
@@ -66,34 +66,34 @@ public void tokenize_noPrefixes_allTakenAsPreamble() {
@Test
public void tokenize_oneArgument() {
// Preamble present
- String argsString = " Some preamble string p/ Argument value ";
- ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash);
+ String argsString = " Some preamble string /p Argument value ";
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, slashP);
assertPreamblePresent(argMultimap, "Some preamble string");
- assertArgumentPresent(argMultimap, pSlash, "Argument value");
+ assertArgumentPresent(argMultimap, slashP, "Argument value");
// No preamble
- argsString = " p/ Argument value ";
- argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash);
+ argsString = " /p Argument value ";
+ argMultimap = ArgumentTokenizer.tokenize(argsString, slashP);
assertPreambleEmpty(argMultimap);
- assertArgumentPresent(argMultimap, pSlash, "Argument value");
+ assertArgumentPresent(argMultimap, slashP, "Argument value");
}
@Test
public void tokenize_multipleArguments() {
// Only two arguments are present
- String argsString = "SomePreambleString -t dashT-Value p/pSlash value";
- ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ);
+ String argsString = "SomePreambleString -t dashT-Value /p pSlash value";
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, slashP, dashT, hatQ);
assertPreamblePresent(argMultimap, "SomePreambleString");
- assertArgumentPresent(argMultimap, pSlash, "pSlash value");
+ assertArgumentPresent(argMultimap, slashP, "pSlash value");
assertArgumentPresent(argMultimap, dashT, "dashT-Value");
assertArgumentAbsent(argMultimap, hatQ);
// All three arguments are present
- argsString = "Different Preamble String ^Q111 -t dashT-Value p/pSlash value";
- argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ);
+ argsString = "Different Preamble String ^Q 111 -t dashT-Value /p pSlash value";
+ argMultimap = ArgumentTokenizer.tokenize(argsString, slashP, dashT, hatQ);
assertPreamblePresent(argMultimap, "Different Preamble String");
- assertArgumentPresent(argMultimap, pSlash, "pSlash value");
+ assertArgumentPresent(argMultimap, slashP, "pSlash value");
assertArgumentPresent(argMultimap, dashT, "dashT-Value");
assertArgumentPresent(argMultimap, hatQ, "111");
@@ -102,15 +102,15 @@ public void tokenize_multipleArguments() {
// Reuse tokenizer on an empty string to ensure ArgumentMultimap is correctly reset
// (i.e. no stale values from the previous tokenizing remain)
argsString = "";
- argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ);
+ argMultimap = ArgumentTokenizer.tokenize(argsString, slashP, dashT, hatQ);
assertPreambleEmpty(argMultimap);
- assertArgumentAbsent(argMultimap, pSlash);
+ assertArgumentAbsent(argMultimap, slashP);
/* Also covers: testing for prefixes not specified as a prefix */
// Prefixes not previously given to the tokenizer should not return any values
argsString = unknownPrefix + "some value";
- argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ);
+ argMultimap = ArgumentTokenizer.tokenize(argsString, slashP, dashT, hatQ);
assertArgumentAbsent(argMultimap, unknownPrefix);
assertPreamblePresent(argMultimap, argsString); // Unknown prefix is taken as part of preamble
}
@@ -118,20 +118,20 @@ public void tokenize_multipleArguments() {
@Test
public void tokenize_multipleArgumentsWithRepeats() {
// Two arguments repeated, some have empty values
- String argsString = "SomePreambleString -t dashT-Value ^Q ^Q -t another dashT value p/ pSlash value -t";
- ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ);
+ String argsString = "SomePreambleString -t dashT-Value ^Q ^Q -t another dashT value /p slashP value -t ";
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, slashP, dashT, hatQ);
assertPreamblePresent(argMultimap, "SomePreambleString");
- assertArgumentPresent(argMultimap, pSlash, "pSlash value");
+ assertArgumentPresent(argMultimap, slashP, "slashP value");
assertArgumentPresent(argMultimap, dashT, "dashT-Value", "another dashT value", "");
assertArgumentPresent(argMultimap, hatQ, "", "");
}
@Test
public void tokenize_multipleArgumentsJoined() {
- String argsString = "SomePreambleStringp/ pSlash joined-tjoined -t not joined^Qjoined";
- ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, pSlash, dashT, hatQ);
- assertPreamblePresent(argMultimap, "SomePreambleStringp/ pSlash joined-tjoined");
- assertArgumentAbsent(argMultimap, pSlash);
+ String argsString = "SomePreambleString/p pSlash joined-tjoined -t not joined^Qjoined";
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(argsString, slashP, dashT, hatQ);
+ assertPreamblePresent(argMultimap, "SomePreambleString/p pSlash joined-tjoined");
+ assertArgumentAbsent(argMultimap, slashP);
assertArgumentPresent(argMultimap, dashT, "not joined^Qjoined");
assertArgumentAbsent(argMultimap, hatQ);
}
diff --git a/src/test/java/seedu/address/logic/parser/AvailableCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AvailableCommandParserTest.java
new file mode 100644
index 00000000000..53b3fbd5e95
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AvailableCommandParserTest.java
@@ -0,0 +1,62 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_TAG_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GROUP;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalTutorialTag.TUES08;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.AvailableCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.TutorialTagContainsGroupPredicate;
+
+public class AvailableCommandParserTest {
+
+ private final AvailableCommandParser parser = new AvailableCommandParser();
+
+ @Test
+ public void parse_incompleteArg_throwsParseException() {
+
+ String invalidTagName = "sfsdfsdff";
+
+ assertParseFailure(parser, " ",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AvailableCommand.MESSAGE_USAGE));
+
+ assertParseFailure(parser, invalidTagName,
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AvailableCommand.MESSAGE_USAGE));
+
+ assertParseFailure(parser, "",
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AvailableCommand.MESSAGE_USAGE));
+
+ assertParseFailure(parser, " /g ",
+ String.format(AvailableCommand.MESSAGE_NON_EMPTY_GROUP_NAME));
+ }
+
+ @Test
+ public void parse_invalidTagName_throwsParseException() {
+
+ String invalidTagName = "sfs dfsd ff";
+
+ assertParseFailure(parser, " /g " + invalidTagName,
+ String.format(MESSAGE_INVALID_TAG_NAME));
+ }
+
+ @Test
+ public void parse_nonemptyPreamble_throwsParseException() throws ParseException {
+ String input = " non-empty preamble " + PREFIX_GROUP + " TUES08";
+ assertParseFailure(parser, input, String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ AvailableCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_validArgs_returnsAvailableCommand() throws ParseException {
+ String args = " " + PREFIX_GROUP + " TUES08";
+ AvailableCommand expectedAvailableCommand = new AvailableCommand(
+ new TutorialTagContainsGroupPredicate("TUES08"), TUES08);
+
+ assertParseSuccess(parser, args, expectedAvailableCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
index 6a40e14a649..d5bf7091be7 100644
--- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
@@ -5,8 +5,13 @@
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
import org.junit.jupiter.api.Test;
+import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.DeleteCommand;
/**
@@ -22,7 +27,9 @@ public class DeleteCommandParserTest {
@Test
public void parse_validArgs_returnsDeleteCommand() {
- assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON));
+ Index[] indexArr = {INDEX_FIRST_PERSON};
+ Set indexSet = new HashSet<>(Arrays.asList(indexArr));
+ assertParseSuccess(parser, "1", new DeleteCommand(indexSet));
}
@Test
diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
index cc7175172d4..4fd4bc13fff 100644
--- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
@@ -1,28 +1,20 @@
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.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;
@@ -38,16 +30,14 @@
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 TAG_EMPTY = " " + PREFIX_TAG + " ";
private static final String MESSAGE_INVALID_FORMAT =
String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE);
@@ -79,6 +69,9 @@ public void parse_invalidPreamble_failure() {
// invalid prefix being parsed as preamble
assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT);
+
+ // attempt to edit tags
+ assertParseFailure(parser, "1" + TAG_EMPTY, MESSAGE_INVALID_FORMAT);
}
@Test
@@ -86,32 +79,26 @@ 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);
+ // attempt to parse tags after a valid field
+ assertParseFailure(parser, "1" + NAME_DESC_AMY + TAG_EMPTY, Name.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser, "1" + NAME_DESC_BOB + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS);
// multiple invalid values, but only the first invalid value is captured
- assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY,
+ assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_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;
+ String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + EMAIL_DESC_AMY + NAME_DESC_AMY;
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();
+ .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).build();
EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
@@ -150,17 +137,6 @@ public void parse_oneFieldSpecified_success() {
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
@@ -179,30 +155,21 @@ public void parse_multipleRepeatedFields_failure() {
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;
+ // multiple valid fields repeated
+ userInput = targetIndex.getOneBased()
+ + PHONE_DESC_AMY + EMAIL_DESC_AMY
+ + PHONE_DESC_AMY + EMAIL_DESC_AMY
+ + PHONE_DESC_BOB + EMAIL_DESC_BOB;
assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL));
// multiple invalid values
- userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC
- + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC;
+ userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_EMAIL_DESC
+ + INVALID_PHONE_DESC + INVALID_EMAIL_DESC;
assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL));
}
- @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/EditTutTagListCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditTutTagListCommandParserTest.java
new file mode 100644
index 00000000000..e90635ee566
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/EditTutTagListCommandParserTest.java
@@ -0,0 +1,67 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_EDIT_TUTORIAL_TAG_FLAG;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_EDIT_TUTORIAL_TAG_FLAG;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIENDS;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND_DESC;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.EditTutTagListCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.Tag;
+
+class EditTutTagListCommandParserTest {
+ private static final String MESSAGE_INVALID_FORMAT =
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTutTagListCommand.MESSAGE_USAGE);
+
+ private EditTutTagListCommandParser parser = new EditTutTagListCommandParser();
+
+ @Test
+ public void parse_emptyArguments_failure() {
+ String emptyArgument = "";
+ assertThrows(ParseException.class, MESSAGE_INVALID_FORMAT, () -> parser.parse(emptyArgument));
+ }
+
+ @Test
+ public void parse_missingParts_failure() {
+
+ // no tagFlag specified
+ assertParseFailure(parser, VALID_TAG_FRIEND_DESC, MESSAGE_INVALID_FORMAT);
+
+ // no tagName specified
+ assertParseFailure(parser, VALID_EDIT_TUTORIAL_TAG_FLAG, MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidFlag_failure() {
+ assertParseFailure(parser, INVALID_EDIT_TUTORIAL_TAG_FLAG + " "
+ + TAG_DESC_FRIEND,
+ MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidValue_failure() {
+ assertParseFailure(parser, VALID_EDIT_TUTORIAL_TAG_FLAG
+ + " " + INVALID_TAG_DESC,
+ Tag.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_validCommand_success() {
+ String userInput = VALID_EDIT_TUTORIAL_TAG_FLAG
+ + " " + VALID_TAG_FRIEND_DESC;
+
+ EditTutTagListCommand expectedCommand = new EditTutTagListCommand(VALID_TAG_FRIENDS,
+ EditTutTagListCommand.CommandSubtype.ADD);
+
+ 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
index d92e64d12f9..5ad0bf37aec 100644
--- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
@@ -1,15 +1,17 @@
package seedu.address.logic.parser;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
import java.util.Arrays;
+import java.util.List;
import org.junit.jupiter.api.Test;
import seedu.address.logic.commands.FindCommand;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.person.FieldContainsKeywordsPredicate;
public class FindCommandParserTest {
@@ -21,14 +23,29 @@ public void parse_emptyArg_throwsParseException() {
}
@Test
- public void parse_validArgs_returnsFindCommand() {
+ public void parse_findName_returnsFindCommand() {
// no leading and trailing whitespaces
+ List keywords = Arrays.asList("Alice", "Bob");
+ FieldContainsKeywordsPredicate[] predicateList = {new FieldContainsKeywordsPredicate(PREFIX_NAME, keywords)};
FindCommand expectedFindCommand =
- new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")));
- assertParseSuccess(parser, "Alice Bob", expectedFindCommand);
+ new FindCommand(Arrays.asList(predicateList));
+ assertParseSuccess(parser, " /n Alice Bob", expectedFindCommand);
// multiple whitespaces between keywords
- assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand);
+ assertParseSuccess(parser, " /n \n Alice \n \t Bob \t", expectedFindCommand);
+ }
+
+ @Test
+ public void parse_findType_returnsFindCommand() {
+ // no leading and trailing whitespaces
+ List keywords = Arrays.asList("stu");
+ FieldContainsKeywordsPredicate[] predicateList = {new FieldContainsKeywordsPredicate(keywords)};
+ FindCommand expectedFindCommand =
+ new FindCommand(Arrays.asList(predicateList));
+ assertParseSuccess(parser, " stu", expectedFindCommand);
+
+ // multiple whitespaces around keywords
+ assertParseSuccess(parser, " stu ", expectedFindCommand);
}
}
diff --git a/src/test/java/seedu/address/logic/parser/MarkCommandParserTest.java b/src/test/java/seedu/address/logic/parser/MarkCommandParserTest.java
new file mode 100644
index 00000000000..bbb3a1e80b9
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/MarkCommandParserTest.java
@@ -0,0 +1,118 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_TAGSTATUS_COMPLETE_GOOD;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_ASS1_ASS2_TAGSTATUS_COMPLETE_GOOD;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_ASS1_MULTIPLE_WHITESPACES_ASS2_TAGSTATUS_COMPLETE_GOOD;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_ASS3_TAGSTATUS_COMPLETE_BAD;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAGNAMES_SET_ASS1_ASS2;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAGNAMES_SET_ASS3;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_1_2_3_SET;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.MarkCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
+
+class MarkCommandParserTest {
+
+ private static final String MESSAGE_INVALID_FORMAT =
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, MarkCommand.MESSAGE_USAGE);
+
+ private MarkCommandParser parser = new MarkCommandParser();
+
+ @Test
+ public void parse_missingParts_failure() {
+ // no index specified
+ assertParseFailure(parser, TAG_ASS1_ASS2_TAGSTATUS_COMPLETE_GOOD, MESSAGE_INVALID_FORMAT);
+
+ // no tag specified
+ assertParseFailure(parser, "1", MESSAGE_INVALID_FORMAT);
+ assertParseFailure(parser, "1 2", MESSAGE_INVALID_FORMAT);
+
+ // no index and no field specified
+ assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidPreamble_failure() {
+ // negative index
+ assertParseFailure(parser, "-5" + TAG_ASS1_ASS2_TAGSTATUS_COMPLETE_GOOD,
+ MESSAGE_INVALID_FORMAT);
+
+ // zero index
+ assertParseFailure(parser, "0" + TAG_ASS1_ASS2_TAGSTATUS_COMPLETE_GOOD,
+ 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_TAG_TAGSTATUS_COMPLETE_GOOD,
+ Tag.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_oneIndex_success() throws ParseException {
+ final Index targetIndex = INDEX_SECOND_PERSON;
+ Set targetIndexSet = new HashSet<>(Arrays.asList(targetIndex));
+
+ // one tag name
+ String userInput = targetIndex.getOneBased() + TAG_ASS3_TAGSTATUS_COMPLETE_BAD;
+ MarkCommand expectedCommand =
+ new MarkCommand(targetIndexSet, VALID_TAGNAMES_SET_ASS3, TagStatus.COMPLETE_BAD);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // multiple tag names
+ userInput = targetIndex.getOneBased() + TAG_ASS1_ASS2_TAGSTATUS_COMPLETE_GOOD;
+ expectedCommand =
+ new MarkCommand(targetIndexSet, VALID_TAGNAMES_SET_ASS1_ASS2, TagStatus.COMPLETE_GOOD);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // multiple taq names separated by multiple whitespaces
+ userInput = targetIndex.getOneBased() + TAG_ASS1_MULTIPLE_WHITESPACES_ASS2_TAGSTATUS_COMPLETE_GOOD;
+ expectedCommand =
+ new MarkCommand(targetIndexSet, VALID_TAGNAMES_SET_ASS1_ASS2, TagStatus.COMPLETE_GOOD);
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_multipleIndices_success() throws ParseException {
+ String targetIndexString = "1 2 3";
+ Set targetIndexSet = INDEX_1_2_3_SET;
+
+ // one tag name
+ String userInput = targetIndexString + TAG_ASS3_TAGSTATUS_COMPLETE_BAD;
+ MarkCommand expectedCommand =
+ new MarkCommand(targetIndexSet, VALID_TAGNAMES_SET_ASS3, TagStatus.COMPLETE_BAD);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // multiple tag names
+ userInput = targetIndexString + TAG_ASS1_ASS2_TAGSTATUS_COMPLETE_GOOD;
+ expectedCommand =
+ new MarkCommand(targetIndexSet, VALID_TAGNAMES_SET_ASS1_ASS2, TagStatus.COMPLETE_GOOD);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // multiple taq names separated by multiple whitespaces
+ userInput = targetIndexString + TAG_ASS1_MULTIPLE_WHITESPACES_ASS2_TAGSTATUS_COMPLETE_GOOD;
+ expectedCommand =
+ new MarkCommand(targetIndexSet, VALID_TAGNAMES_SET_ASS1_ASS2, TagStatus.COMPLETE_GOOD);
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+}
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/logic/parser/RemovetagCommandParserTest.java b/src/test/java/seedu/address/logic/parser/RemovetagCommandParserTest.java
new file mode 100644
index 00000000000..59c10fb93b0
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/RemovetagCommandParserTest.java
@@ -0,0 +1,70 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAGNAMES_SET_FRIENDS;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND_DESC;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_2_SET;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.RemovetagCommand;
+import seedu.address.model.tag.Tag;
+
+class RemovetagCommandParserTest {
+ private static final String MESSAGE_INVALID_FORMAT =
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, RemovetagCommand.MESSAGE_USAGE);
+
+ private RemovetagCommandParser parser = new RemovetagCommandParser();
+
+ @Test
+ public void parse_missingParts_failure() {
+ // no index specified
+ assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT);
+
+ // no tag specified
+ assertParseFailure(parser, "1", MESSAGE_INVALID_FORMAT);
+
+ // no index and no field specified
+ assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT);
+ }
+
+ @Test
+ public void parse_invalidPreamble_failure() {
+ // negative index
+ assertParseFailure(parser, "-5" + TAG_DESC_FRIEND,
+ MESSAGE_INVALID_FORMAT);
+
+ // zero index
+ assertParseFailure(parser, "0" + TAG_DESC_FRIEND,
+ 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_TAG_DESC,
+ Tag.MESSAGE_CONSTRAINTS);
+ }
+
+ @Test
+ public void parse_validCommand_success() {
+ Index targetIndex = INDEX_SECOND_PERSON;
+ String userInput = targetIndex.getOneBased() + VALID_TAG_FRIEND_DESC;
+
+ RemovetagCommand expectedCommand = new RemovetagCommand(INDEX_2_SET, VALID_TAGNAMES_SET_FRIENDS);
+
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/StatefulParserUtilTest.java b/src/test/java/seedu/address/logic/parser/StatefulParserUtilTest.java
new file mode 100644
index 00000000000..4245c233cc4
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/StatefulParserUtilTest.java
@@ -0,0 +1,216 @@
+package seedu.address.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.parser.StatefulParserUtil.MESSAGE_INVALID_INDEX;
+import static seedu.address.logic.parser.StatefulParserUtil.isCreatingNewTag;
+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.commands.EditTutTagListCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.PersonType;
+import seedu.address.model.person.Phone;
+import seedu.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
+
+public class StatefulParserUtilTest {
+ private static final String INVALID_NAME = "R@chel";
+ private static final String INVALID_PHONE = "+651234";
+ 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, () -> StatefulParserUtil.parseIndex("10 a"));
+ }
+
+ @Test
+ public void parseIndex_outOfRangeInput_throwsParseException() {
+ assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, ()
+ -> StatefulParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1)));
+ }
+
+ @Test
+ public void parsePersonType_invalidInput_throwsParseException() {
+ assertThrows(ParseException.class, () -> StatefulParserUtil.parsePersonType("prof"));
+ }
+
+ @Test
+ public void parsePersonType_validInput_success() throws Exception {
+ assertEquals(PersonType.STU, StatefulParserUtil.parsePersonType(" stu"));
+ }
+
+ @Test
+ public void parsePersonType_nullInput_success() throws Exception {
+ assertEquals(PersonType.STU, StatefulParserUtil.parsePersonType(" "));
+ }
+
+ @Test
+ public void parseFindPersonType_invalidInput_throwsParseException() {
+ assertThrows(ParseException.class, () -> StatefulParserUtil.parseFindPersonType("prof"));
+ }
+
+ @Test
+ public void parseFindPersonType_validInput_success() throws Exception {
+ assertEquals(PersonType.STU, StatefulParserUtil.parseFindPersonType(" stu"));
+ }
+
+ @Test
+ public void parseFindPersonType_nullInput_success() throws Exception {
+ assertNull(StatefulParserUtil.parseFindPersonType(" "));
+ }
+
+ @Test
+ public void parseIndex_validInput_success() throws Exception {
+ // No whitespaces
+ assertEquals(INDEX_FIRST_PERSON, StatefulParserUtil.parseIndex("1"));
+
+ // Leading and trailing whitespaces
+ assertEquals(INDEX_FIRST_PERSON, StatefulParserUtil.parseIndex(" 1 "));
+ }
+
+ @Test
+ public void parseName_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> StatefulParserUtil.parseName((String) null));
+ }
+
+ @Test
+ public void parseName_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> StatefulParserUtil.parseName(INVALID_NAME));
+ }
+
+ @Test
+ public void parseName_validValueWithoutWhitespace_returnsName() throws Exception {
+ Name expectedName = new Name(VALID_NAME);
+ assertEquals(expectedName, StatefulParserUtil.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, StatefulParserUtil.parseName(nameWithWhitespace));
+ }
+
+ @Test
+ public void parsePhone_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> StatefulParserUtil.parsePhone((String) null));
+ }
+
+ @Test
+ public void parsePhone_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> StatefulParserUtil.parsePhone(INVALID_PHONE));
+ }
+
+ @Test
+ public void parsePhone_validValueWithoutWhitespace_returnsPhone() throws Exception {
+ Phone expectedPhone = new Phone(VALID_PHONE);
+ assertEquals(expectedPhone, StatefulParserUtil.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, StatefulParserUtil.parsePhone(phoneWithWhitespace));
+ }
+
+ @Test
+ public void parseEmail_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> StatefulParserUtil.parseEmail((String) null));
+ }
+
+ @Test
+ public void parseEmail_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> StatefulParserUtil.parseEmail(INVALID_EMAIL));
+ }
+
+ @Test
+ public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception {
+ Email expectedEmail = new Email(VALID_EMAIL);
+ assertEquals(expectedEmail, StatefulParserUtil.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, StatefulParserUtil.parseEmail(emailWithWhitespace));
+ }
+
+ @Test
+ public void parseTag_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> StatefulParserUtil.parseTag(null));
+ }
+
+ @Test
+ public void parseTag_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> StatefulParserUtil.parseTag(INVALID_TAG));
+ }
+
+ @Test
+ public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception {
+ Tag expectedTag = Tag.createTag(VALID_TAG_1, TagStatus.COMPLETE_GOOD);
+ assertEquals(expectedTag, StatefulParserUtil.parseTag(VALID_TAG_1));
+ }
+
+ @Test
+ public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception {
+ String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE;
+ Tag expectedTag = Tag.createTag(VALID_TAG_1, TagStatus.COMPLETE_GOOD);
+ assertEquals(expectedTag, StatefulParserUtil.parseTag(tagWithWhitespace));
+ }
+
+ @Test
+ public void parseTags_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> StatefulParserUtil.parseTags(null));
+ }
+
+ @Test
+ public void parseTags_collectionWithInvalidTags_throwsParseException() {
+ assertThrows(ParseException.class, () -> StatefulParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG)));
+ }
+
+ @Test
+ public void parseTags_emptyCollection_returnsEmptySet() throws Exception {
+ assertTrue(StatefulParserUtil.parseTags(Collections.emptyList()).isEmpty());
+ }
+
+ @Test
+ public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception {
+ Set actualTagSet = StatefulParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2));
+ Set expectedTagSet = new HashSet(Arrays.asList(Tag.createTag(VALID_TAG_1, TagStatus.DEFAULT_STATUS),
+ Tag.createTag(VALID_TAG_2, TagStatus.DEFAULT_STATUS)));
+
+ assertEquals(expectedTagSet, actualTagSet);
+ }
+
+ @Test
+ public void parseEditTutTagListCommandFlag() throws Exception {
+ assert(isCreatingNewTag(EditTutTagListCommand.ADD_FLAG) == EditTutTagListCommand.CommandSubtype.ADD);
+ assert(isCreatingNewTag(EditTutTagListCommand.DELETE_FLAG) == EditTutTagListCommand.CommandSubtype.DELETE);
+ assert(isCreatingNewTag(EditTutTagListCommand.LIST_FLAG) == EditTutTagListCommand.CommandSubtype.LIST);
+ String invalidCommandFlag = "";
+ assertThrows(ParseException.class, () -> isCreatingNewTag(invalidCommandFlag));
+ }
+}
diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java
index 68c8c5ba4d5..5b8ab5d9192 100644
--- a/src/test/java/seedu/address/model/AddressBookTest.java
+++ b/src/test/java/seedu/address/model/AddressBookTest.java
@@ -3,11 +3,11 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.ALICE;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import static seedu.address.testutil.TypicalTutorialTag.WED10;
import java.util.Arrays;
import java.util.Collection;
@@ -20,6 +20,8 @@
import javafx.collections.ObservableList;
import seedu.address.model.person.Person;
import seedu.address.model.person.exceptions.DuplicatePersonException;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TutorialTag;
import seedu.address.testutil.PersonBuilder;
public class AddressBookTest {
@@ -46,7 +48,7 @@ public void resetData_withValidReadOnlyAddressBook_replacesData() {
@Test
public void resetData_withDuplicatePersons_throwsDuplicatePersonException() {
// Two persons with the same identity fields
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
+ Person editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND)
.build();
List newPersons = Arrays.asList(ALICE, editedAlice);
AddressBookStub newData = new AddressBookStub(newPersons);
@@ -73,7 +75,7 @@ public void hasPerson_personInAddressBook_returnsTrue() {
@Test
public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() {
addressBook.addPerson(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
+ Person editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND)
.build();
assertTrue(addressBook.hasPerson(editedAlice));
}
@@ -83,12 +85,75 @@ public void getPersonList_modifyList_throwsUnsupportedOperationException() {
assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0));
}
+ ///////
+ @Test
+ public void hasTutorialTag_nullTutorialTag_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> addressBook.hasTutorialTag(null));
+ }
+
+ @Test
+ public void hasTutorialTag_tutorialTagNotInAddressBook_returnsFalse() {
+ assertFalse(addressBook.hasTutorialTag(WED10));
+ }
+
+ @Test
+ public void hasTutorialTag_tutorialTagInAddressBook_returnsTrue() {
+ addressBook.addTutorialTag(WED10);
+ assertTrue(addressBook.hasTutorialTag(WED10));
+ }
+
+ @Test
+ public void hasTutorialTag_tutorialTagWithSameTagNamesInAddressBook_returnsTrue() {
+ addressBook.addTutorialTag(WED10);
+ TutorialTag editedWed10 = new TutorialTag("WED10", TagStatus.AVAILABLE);
+ assertTrue(addressBook.hasTutorialTag(editedWed10));
+ }
+
+ @Test
+ public void deleteTutorialTag_success() {
+ addressBook.addTutorialTag(WED10);
+ addressBook.removeTutorialTag(WED10);
+ assertFalse(addressBook.hasTutorialTag(WED10));
+ }
+
+ @Test
+ public void getTutoiralList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () -> addressBook.getTutorialTagList().remove(0));
+ }
+ ///////
+
@Test
public void toStringMethod() {
String expected = AddressBook.class.getCanonicalName() + "{persons=" + addressBook.getPersonList() + "}";
assertEquals(expected, addressBook.toString());
}
+ @Test
+ void equals_selfAddressbook_true() {
+ assert(addressBook.equals(addressBook));
+ }
+
+ @Test
+ void equals_otherTypeObject_false() {
+ assertFalse(addressBook.equals(new Object()));
+ }
+
+ @Test
+ void hashcode_staysTheSameForSameAddressBook() {
+ assertEquals(addressBook.hashCode(), addressBook.hashCode());
+ }
+
+ @Test
+ void getTutorialTagListString_forEmptyAddressBook() {
+ assertEquals(addressBook.getTutorialTagListString(), "Available Tutorial Tag(s): [ ]");
+ }
+
+ @Test
+ void getTutorialTagListString_forNonEmptyAddressBook() {
+ addressBook.addTutorialTag(WED10);
+ assertEquals(addressBook.getTutorialTagListString(), "Available Tutorial Tag(s): [WED10]");
+ }
+
/**
* A stub ReadOnlyAddressBook whose persons list can violate interface constraints.
*/
@@ -103,6 +168,9 @@ private static class AddressBookStub implements ReadOnlyAddressBook {
public ObservableList getPersonList() {
return persons;
}
+ @Override
+ public ObservableList getTutorialTagList() {
+ return getTutorialTagList();
+ };
}
-
}
diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java
index 2cf1418d116..bf1eed7ea2d 100644
--- a/src/test/java/seedu/address/model/ModelManagerTest.java
+++ b/src/test/java/seedu/address/model/ModelManagerTest.java
@@ -7,6 +7,7 @@
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.ALICE;
import static seedu.address.testutil.TypicalPersons.BENSON;
+import static seedu.address.testutil.TypicalTutorialTag.WED10;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -15,7 +16,8 @@
import org.junit.jupiter.api.Test;
import seedu.address.commons.core.GuiSettings;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.logic.commands.EditTutTagListCommand;
+import seedu.address.model.person.FieldContainsKeywordsPredicate;
import seedu.address.testutil.AddressBookBuilder;
public class ModelManagerTest {
@@ -118,7 +120,7 @@ public void equals() {
// different filteredList -> returns false
String[] keywords = ALICE.getName().fullName.split("\\s+");
- modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords)));
+ modelManager.updateFilteredPersonList(new FieldContainsKeywordsPredicate(Arrays.asList(keywords)));
assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs)));
// resets modelManager to initial state for upcoming tests
@@ -129,4 +131,37 @@ public void equals() {
differentUserPrefs.setAddressBookFilePath(Paths.get("differentFilePath"));
assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs)));
}
+
+ @Test
+ public void hasTutorialTag_nullTutorialTag_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> modelManager.hasTutorialTag(null));
+ }
+
+ @Test
+ public void hasTutorialTag_tutorialTagNotInAddressBook_returnsFalse() {
+ assertFalse(modelManager.hasTutorialTag(WED10));
+ }
+
+ @Test
+ public void hasTutorialTag_tutorialTagInAddressBook_returnsTrue() {
+ modelManager.addTutorialTag(WED10);
+ assertTrue(modelManager.hasTutorialTag(WED10));
+ }
+
+ @Test
+ public void deleteTutorialTag_success() {
+ modelManager.addTutorialTag(WED10);
+ modelManager.deleteTutorialTag(WED10);
+ assertFalse(modelManager.hasTutorialTag(WED10));
+ }
+
+ @Test
+ public void getFilteredTutorialTagList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, () -> modelManager.getTutorialTagList().remove(0));
+ }
+
+ @Test
+ public void getTutorialTagListString() {
+ assertEquals(modelManager.getTutorialTagListString(), EditTutTagListCommand.EMPTY_TUTORIALTAGLIST_OUTPUT);
+ }
}
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/FieldContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/FieldContainsKeywordsPredicateTest.java
new file mode 100644
index 00000000000..271888ba9ab
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/FieldContainsKeywordsPredicateTest.java
@@ -0,0 +1,106 @@
+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.parser.CliSyntax.PREFIX_NAME;
+
+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 FieldContainsKeywordsPredicateTest {
+
+ @Test
+ public void equals() {
+ List firstPredicateKeywordList = Collections.singletonList("first");
+ List secondPredicateKeywordList = Arrays.asList("first", "second");
+
+ FieldContainsKeywordsPredicate firstPredicate = new FieldContainsKeywordsPredicate(firstPredicateKeywordList);
+ FieldContainsKeywordsPredicate secondPredicate = new FieldContainsKeywordsPredicate(secondPredicateKeywordList);
+
+ // same object -> returns true
+ assertTrue(firstPredicate.equals(firstPredicate));
+
+ // same values -> returns true
+ FieldContainsKeywordsPredicate firstPredicateCopy =
+ new FieldContainsKeywordsPredicate(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
+ FieldContainsKeywordsPredicate predicate = new FieldContainsKeywordsPredicate(PREFIX_NAME,
+ Collections.singletonList("Alice"));
+ assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
+
+
+ // Multiple keywords
+ predicate = new FieldContainsKeywordsPredicate(PREFIX_NAME, Arrays.asList("Alice", "Bob"));
+ assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
+
+ // Only one matching keyword
+ predicate = new FieldContainsKeywordsPredicate(PREFIX_NAME, Arrays.asList("Bob", "Carol"));
+ assertTrue(predicate.test(new PersonBuilder().withName("Alice Carol").build()));
+
+ // Mixed-case keywords
+ predicate = new FieldContainsKeywordsPredicate(PREFIX_NAME, Arrays.asList("aLIce", "bOB"));
+ assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build()));
+
+ }
+
+ @Test
+ public void test_nameDoesNotContainKeywords_returnsFalse() {
+ // Zero keywords
+ FieldContainsKeywordsPredicate predicate = new FieldContainsKeywordsPredicate(Collections.emptyList());
+ assertFalse(predicate.test(new PersonBuilder().withName("Alice").build()));
+
+ // Non-matching keyword
+ predicate = new FieldContainsKeywordsPredicate(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 FieldContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street"));
+ assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345")
+ .withEmail("alice@email.com").build()));
+ }
+
+ @Test
+ public void test_tagDoesNotContainKeywords_returnsFalse() {
+ // Zero keywords
+ FieldContainsKeywordsPredicate predicate = new FieldContainsKeywordsPredicate(Collections.emptyList());
+ assertFalse(predicate.test(new PersonBuilder().withTags("tag1").build()));
+
+ // Non-matching keyword
+ predicate = new FieldContainsKeywordsPredicate(Arrays.asList("tag2"));
+ assertFalse(predicate.test(new PersonBuilder().withTags("tag1").build()));
+
+ // Keywords match phone, email and address, but does not match name
+ predicate = new FieldContainsKeywordsPredicate(Arrays.asList("tag1", "tag2"));
+ assertFalse(predicate.test(new PersonBuilder().withTags("tag1").build()));
+ }
+
+ @Test
+ public void toStringMethod() {
+ List keywords = List.of("keyword1", "keyword2");
+ FieldContainsKeywordsPredicate predicate = new FieldContainsKeywordsPredicate(PREFIX_NAME, keywords);
+
+ String expected = FieldContainsKeywordsPredicate.class.getCanonicalName() + "{" + PREFIX_NAME
+ + " keywords=" + keywords + "}";
+ assertEquals(expected, predicate.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/IdTest.java b/src/test/java/seedu/address/model/person/IdTest.java
new file mode 100644
index 00000000000..5622008755e
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/IdTest.java
@@ -0,0 +1,54 @@
+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 IdTest {
+
+ @Test
+ public void constructor_nullId_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Id(null));
+ }
+
+ @Test
+ public void constructor_invalidId_throwsIllegalArgumentException() {
+ String invalidId = "123456789"; // invalid because it does not start and end with a letter
+ assertThrows(IllegalArgumentException.class, () -> new Id(invalidId));
+ }
+
+ @Test
+ public void isValidId() {
+ // valid ids
+ assertTrue(Id.isValidId("A1234567Z"));
+ assertTrue(Id.isValidId("B9876543X"));
+
+ // invalid ids
+ assertFalse(Id.isValidId("123456789")); // does not start and end with a letter
+ assertFalse(Id.isValidId("A123456789")); // more than 7 digits
+ assertFalse(Id.isValidId("A12345X")); // less than 7 digits
+ assertFalse(Id.isValidId("A12345 X")); // contains space
+ }
+
+ @Test
+ public void equals() {
+ Id id1 = new Id("A1234567Z");
+ Id id3 = new Id("B9876543X");
+
+ // same object -> returns true
+ assertTrue(id1.equals(id1));
+
+ // same values -> returns true
+ assertTrue(id1.equals(new Id("A1234567Z")));
+
+ // different values -> returns false
+ assertFalse(id1.equals(id3));
+
+ // null -> returns false
+ assertFalse(id1.equals(null));
+
+ // different types -> returns false
+ assertFalse(id1.equals("A1234567Z"));
+ }
+}
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/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java
index 31a10d156c9..06cc5cc44e7 100644
--- a/src/test/java/seedu/address/model/person/PersonTest.java
+++ b/src/test/java/seedu/address/model/person/PersonTest.java
@@ -2,9 +2,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
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_ID_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;
@@ -32,68 +33,73 @@ public void isSamePerson() {
// 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));
+ // same name, id, all other attributes different -> returns true
+ Person editedPerson = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB)
+ .withTags(VALID_TAG_HUSBAND).build();
+ assertTrue(ALICE.isSamePerson(editedPerson));
- // different name, all other attributes same -> returns false
- editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
- assertFalse(ALICE.isSamePerson(editedAlice));
+ // different name, all other attributes same -> returns true
+ editedPerson = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
+ assertTrue(ALICE.isSamePerson(editedPerson));
- // 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));
+ // different id, all other attributes same -> returns false
+ editedPerson = new PersonBuilder(ALICE).withId(VALID_ID_BOB).build();
+ assertFalse(ALICE.isSamePerson(editedPerson));
}
@Test
public void equals() {
// same values -> returns true
Person aliceCopy = new PersonBuilder(ALICE).build();
- assertTrue(ALICE.equals(aliceCopy));
+ assertEquals(ALICE, aliceCopy);
// same object -> returns true
- assertTrue(ALICE.equals(ALICE));
+ assertEquals(ALICE, ALICE);
// null -> returns false
- assertFalse(ALICE.equals(null));
+ assertNotEquals(null, ALICE);
// different type -> returns false
- assertFalse(ALICE.equals(5));
+ assertNotEquals(5, ALICE);
// different person -> returns false
- assertFalse(ALICE.equals(BOB));
+ assertNotEquals(ALICE, BOB);
// different name -> returns false
Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
+ assertNotEquals(ALICE, editedAlice);
// different phone -> returns false
editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
+ assertNotEquals(ALICE, 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));
+ assertNotEquals(ALICE, editedAlice);
// different tags -> returns false
editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build();
- assertFalse(ALICE.equals(editedAlice));
+ assertNotEquals(ALICE, editedAlice);
+ }
+
+ @Test
+ public void testHashCode() {
+ // Hash codes of same objects must be the same
+ Person aliceCopy = new PersonBuilder(ALICE).build();
+ assertEquals(ALICE.hashCode(), aliceCopy.hashCode());
+
+ // Hash codes of different objects should be different
+ assertNotEquals(ALICE.hashCode(), BOB.hashCode());
}
@Test
public void toStringMethod() {
- String expected = Person.class.getCanonicalName() + "{name=" + ALICE.getName() + ", phone=" + ALICE.getPhone()
- + ", email=" + ALICE.getEmail() + ", address=" + ALICE.getAddress() + ", tags=" + ALICE.getTags() + "}";
+ String expected =
+ Student.class.getCanonicalName() + "{type=" + ALICE.getType() + ", name=" + ALICE.getName()
+ + ", id=" + ALICE.getId()
+ + ", phone=" + ALICE.getPhone()
+ + ", email=" + ALICE.getEmail()
+ + ", tags=" + ALICE.getTags() + "}";
assertEquals(expected, ALICE.toString());
}
}
diff --git a/src/test/java/seedu/address/model/person/TaTest.java b/src/test/java/seedu/address/model/person/TaTest.java
new file mode 100644
index 00000000000..72ba2184e0d
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/TaTest.java
@@ -0,0 +1,38 @@
+package seedu.address.model.person;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
+import static seedu.address.testutil.TypicalPersons.FIONA;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.PersonBuilder;
+
+class TaTest {
+
+ @Test
+ void testEquals() {
+ // same value
+ Ta fionaCopy = (Ta) new PersonBuilder(FIONA).build();
+ assertEquals(fionaCopy, FIONA);
+
+ // same object
+ assertEquals(FIONA, FIONA);
+
+ // different name
+ Ta editedFiona = (Ta) new PersonBuilder(FIONA).withName(VALID_NAME_BOB).build();
+ assertNotEquals(editedFiona, FIONA);
+ }
+
+ @Test
+ void testToString() {
+ String expected =
+ Ta.class.getCanonicalName() + "{type=" + FIONA.getType() + ", name=" + FIONA.getName()
+ + ", id=" + FIONA.getId()
+ + ", phone=" + FIONA.getPhone()
+ + ", email=" + FIONA.getEmail()
+ + ", tags=" + FIONA.getTags() + "}";
+ assertEquals(expected, FIONA.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/TutorialTagContainsGroupPredicateTest.java b/src/test/java/seedu/address/model/person/TutorialTagContainsGroupPredicateTest.java
new file mode 100644
index 00000000000..66dc9e1e774
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/TutorialTagContainsGroupPredicateTest.java
@@ -0,0 +1,39 @@
+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.TypicalPersons.JANE;
+
+import org.junit.jupiter.api.Test;
+
+
+public class TutorialTagContainsGroupPredicateTest {
+
+ @Test
+ public void equals() {
+ TutorialTagContainsGroupPredicate predicate1 = new TutorialTagContainsGroupPredicate("TUES08");
+ TutorialTagContainsGroupPredicate predicate2 = new TutorialTagContainsGroupPredicate("TUES09");
+
+ // same object -> returns true
+ assertTrue(predicate1.equals(predicate1));
+
+ // same values -> returns true
+ TutorialTagContainsGroupPredicate predicate1Copy = new TutorialTagContainsGroupPredicate("TUES08");
+ assertTrue(predicate1.equals(predicate1Copy));
+
+ // different types -> returns false
+ assertFalse(predicate1.equals(1));
+
+ // null -> returns false
+ assertFalse(predicate1.equals(null));
+
+ // different tutorial group -> returns false
+ assertFalse(predicate1.equals(predicate2));
+ }
+
+ @Test
+ public void test_student_returnsFalse() {
+ TutorialTagContainsGroupPredicate predicate = new TutorialTagContainsGroupPredicate("W10");
+ assertFalse(predicate.test(JANE));
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
index 17ae501df08..6a866223e8d 100644
--- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java
+++ b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
@@ -3,7 +3,6 @@
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;
@@ -42,7 +41,7 @@ public void contains_personInList_returnsTrue() {
@Test
public void contains_personWithSameIdentityFieldsInList_returnsTrue() {
uniquePersonList.add(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
+ Person editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND)
.build();
assertTrue(uniquePersonList.contains(editedAlice));
}
@@ -85,7 +84,7 @@ public void setPerson_editedPersonIsSamePerson_success() {
@Test
public void setPerson_editedPersonHasSameIdentity_success() {
uniquePersonList.add(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
+ Person editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND)
.build();
uniquePersonList.setPerson(ALICE, editedAlice);
UniquePersonList expectedUniquePersonList = new UniquePersonList();
diff --git a/src/test/java/seedu/address/model/person/UniqueTutorialTagListTest.java b/src/test/java/seedu/address/model/person/UniqueTutorialTagListTest.java
new file mode 100644
index 00000000000..afd9154feb4
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/UniqueTutorialTagListTest.java
@@ -0,0 +1,152 @@
+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.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalTutorialTag.WED10;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.person.exceptions.DuplicateTutorialTagException;
+import seedu.address.model.person.exceptions.TutorialTagNotFoundException;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TutorialTag;
+
+class UniqueTutorialTagListTest {
+ private final UniqueTutorialTagList uniqueTutorialTagList = new UniqueTutorialTagList();
+
+ @Test
+ public void contains_nullTutorialTag_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueTutorialTagList.contains(null));
+ }
+
+ @Test
+ public void contains_tutorialTagNotInList_returnsFalse() {
+ assertFalse(uniqueTutorialTagList.contains(WED10));
+ }
+
+ @Test
+ public void contains_tutorialTagInList_returnsTrue() {
+ uniqueTutorialTagList.add(WED10);
+ assertTrue(uniqueTutorialTagList.contains(WED10));
+ }
+
+ @Test
+ public void contains_tutorialTagWithSameIdentityFieldsInList_returnsTrue() {
+ uniqueTutorialTagList.add(WED10);
+ TutorialTag editedWed10 = new TutorialTag("WED10", TagStatus.AVAILABLE);
+ assertTrue(uniqueTutorialTagList.contains(editedWed10));
+ }
+
+ @Test
+ public void add_nullTutorialTag_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueTutorialTagList.add(null));
+ }
+
+ @Test
+ public void add_duplicateTutorialTag_throwsDuplicatePersonException() {
+ uniqueTutorialTagList.add(WED10);
+ assertThrows(DuplicateTutorialTagException.class, () -> uniqueTutorialTagList.add(WED10));
+ }
+
+ @Test
+ public void remove_nullTutorialTag_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueTutorialTagList.remove(null));
+ }
+
+ @Test
+ public void remove_tutorialTagDoesNotExist_throwsPersonNotFoundException() {
+ assertThrows(TutorialTagNotFoundException.class, () -> uniqueTutorialTagList.remove(WED10));
+ }
+
+ @Test
+ public void remove_existingPerson_removesPerson() {
+ uniqueTutorialTagList.add(WED10);
+ uniqueTutorialTagList.remove(WED10);
+ UniqueTutorialTagList expectedUniqueTutorialTagList = new UniqueTutorialTagList();
+ assertEquals(expectedUniqueTutorialTagList, uniqueTutorialTagList);
+ }
+
+ @Test
+ public void setTutorialTags_nullUniqueTutorialTagList_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueTutorialTagList
+ .setTutorialTags((UniqueTutorialTagList) null));
+ }
+
+ @Test
+ public void setTutorialTags_uniqueTutorialTagList_replacesOwnListWithProvidedUniqueTutorialTagList() {
+ uniqueTutorialTagList.add(WED10);
+ UniqueTutorialTagList expectedUniqueTutorialTagList = new UniqueTutorialTagList();
+ expectedUniqueTutorialTagList.add(WED10);
+ uniqueTutorialTagList.setTutorialTags(expectedUniqueTutorialTagList);
+ assertEquals(expectedUniqueTutorialTagList, uniqueTutorialTagList);
+ }
+
+ @Test
+ public void setTutorialTags_nullList_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> uniqueTutorialTagList
+ .setTutorialTags((List) null));
+ }
+
+ @Test
+ public void setTutorialTags_listWithDuplicateTutorialTags_throwsDuplicatePersonException() {
+ List listWithDuplicateTutorialTags = Arrays.asList(WED10, WED10);
+ assertThrows(DuplicateTutorialTagException.class, () -> uniqueTutorialTagList
+ .setTutorialTags(listWithDuplicateTutorialTags));
+ }
+
+ @Test
+ public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() {
+ assertThrows(UnsupportedOperationException.class, ()
+ -> uniqueTutorialTagList.asUnmodifiableObservableList().remove(0));
+ }
+
+ @Test
+ public void toStringMethod() {
+ assertEquals(uniqueTutorialTagList.asUnmodifiableObservableList().toString(),
+ uniqueTutorialTagList.toString());
+ }
+
+ @Test
+ public void return_iterator_success() {
+ assert (uniqueTutorialTagList.iterator() instanceof Iterator);
+ }
+
+ @Test
+ public void equals_selfUniqueTutoiralTagList() {
+ assert uniqueTutorialTagList.equals(uniqueTutorialTagList);
+ }
+
+ @Test
+ public void equals_nullUniqueTutoiralTagList_fail() {
+ assertFalse(uniqueTutorialTagList.equals(null));
+ }
+
+ @Test
+ public void equals_nonUniqueTutoiralTagListObject_fail() {
+ assertFalse(uniqueTutorialTagList.equals(new Object()));
+ }
+
+ @Test
+ public void sameHashCode_forSameUniqueTutorialTagList() {
+ UniqueTutorialTagList list1 = new UniqueTutorialTagList();
+ UniqueTutorialTagList list2 = new UniqueTutorialTagList();
+ list1.add(WED10);
+ list2.add(WED10);
+ assertEquals(list1.hashCode(), list2.hashCode());
+ }
+
+ @Test
+ public void differentHashCode_forDifferentUniqueTutorialTagList() {
+ UniqueTutorialTagList list1 = new UniqueTutorialTagList();
+ UniqueTutorialTagList list2 = new UniqueTutorialTagList();
+ list1.add(WED10);
+ assertNotEquals(list1.hashCode(), list2.hashCode());
+ }
+}
diff --git a/src/test/java/seedu/address/model/tag/TagStatusTest.java b/src/test/java/seedu/address/model/tag/TagStatusTest.java
new file mode 100644
index 00000000000..cb88b8c8f25
--- /dev/null
+++ b/src/test/java/seedu/address/model/tag/TagStatusTest.java
@@ -0,0 +1,39 @@
+package seedu.address.model.tag;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.Assert;
+
+class TagStatusTest {
+
+ @Test
+ void getTagStatus() {
+
+ final String emptyTagStatus = "";
+
+ assertTrue(TagStatus.COMPLETE_GOOD.equals(TagStatus.getTagStatus(
+ TagStatus.COMPLETE_GOOD_KEYWORD)));
+ assertTrue(TagStatus.COMPLETE_BAD.equals(TagStatus.getTagStatus(
+ TagStatus.COMPLETE_BAD_KEYWORD)));
+ assertTrue(TagStatus.INCOMPLETE_GOOD.equals(TagStatus.getTagStatus(
+ TagStatus.INCOMPLETE_GOOD_KEYWORD)));
+ assertTrue(TagStatus.INCOMPLETE_BAD.equals(TagStatus.getTagStatus(
+ TagStatus.INCOMPLETE_BAD_KEYWORD)));
+
+ assertTrue(TagStatus.PRESENT.equals(TagStatus.getTagStatus(
+ TagStatus.PRESENT_KEYWORD)));
+ assertTrue(TagStatus.ABSENT.equals(TagStatus.getTagStatus(
+ TagStatus.ABSENT_KEYWORD)));
+ assertTrue(TagStatus.ABSENT_WITH_REASON.equals(TagStatus.getTagStatus(
+ TagStatus.ABSENT_WITH_REASON_KEYWORD)));
+
+ assertTrue(TagStatus.ASSIGNED.equals(TagStatus.getTagStatus(
+ TagStatus.ASSIGNED_KEYWORD)));
+ assertTrue(TagStatus.AVAILABLE.equals(TagStatus.getTagStatus(
+ TagStatus.AVAILABLE_KEYWORD)));
+
+ Assert.assertThrows(IllegalArgumentException.class, () -> TagStatus.getTagStatus(emptyTagStatus));
+ }
+}
diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/seedu/address/model/tag/TagTest.java
index 64d07d79ee2..a7fce9ecc6c 100644
--- a/src/test/java/seedu/address/model/tag/TagTest.java
+++ b/src/test/java/seedu/address/model/tag/TagTest.java
@@ -7,14 +7,27 @@
public class TagTest {
@Test
- public void constructor_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new Tag(null));
+ public void constructor_nullTagName_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> Tag.createTag(null, TagStatus.INCOMPLETE_GOOD));
+ }
+
+ @Test
+ public void constructor_nullTagStatus_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> Tag.createTag("Test", null));
}
@Test
public void constructor_invalidTagName_throwsIllegalArgumentException() {
String invalidTagName = "";
- assertThrows(IllegalArgumentException.class, () -> new Tag(invalidTagName));
+ assertThrows(IllegalArgumentException.class, () -> Tag.createTag(invalidTagName, TagStatus.INCOMPLETE_GOOD));
+ }
+
+ @Test
+ public void constructor_validTagNameAndValidTagStatus_success() {
+ String validTagName = "test";
+ assert(Tag.createTag(validTagName, TagStatus.COMPLETE_GOOD) instanceof AssignmentTag);
+ assert(Tag.createTag(validTagName, TagStatus.PRESENT) instanceof AttendanceTag);
+ assert(Tag.createTag(validTagName, TagStatus.ASSIGNED) instanceof TutorialTag);
}
@Test
@@ -23,4 +36,33 @@ public void isValidTagName() {
assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null));
}
+ @Test
+ public void correct_tagType() {
+ String validTagName = "test";
+ assert(Tag.createTag(validTagName, TagStatus.COMPLETE_GOOD).isAssignment());
+ assert(Tag.createTag(validTagName, TagStatus.PRESENT).isAttendance());
+ assert(Tag.createTag(validTagName, TagStatus.ASSIGNED).isTutorial());
+ }
+
+ @Test
+ public void equals_tagsWithTheSameTagName_success() {
+ String validTagName = "test";
+ Tag tag1 = Tag.createTag(validTagName, TagStatus.DEFAULT_STATUS);
+ Tag tag2 = Tag.createTag(validTagName, TagStatus.DEFAULT_STATUS);
+ assert(tag1.equals(tag2));
+ }
+
+ @Test
+ public void equals_theSameTag_success() {
+ String validTagName = "test";
+ Tag tag1 = Tag.createTag(validTagName, TagStatus.DEFAULT_STATUS);
+ assert(tag1.equals(tag1));
+ }
+
+ @Test
+ public void equals_tagNotEqualToNonTagObjects_success() {
+ String validTagName = "test";
+ Tag tag1 = Tag.createTag(validTagName, TagStatus.DEFAULT_STATUS);
+ assert(!tag1.equals(new Object()));
+ }
}
diff --git a/src/test/java/seedu/address/model/tag/TutorialTagTest.java b/src/test/java/seedu/address/model/tag/TutorialTagTest.java
new file mode 100644
index 00000000000..3110902058a
--- /dev/null
+++ b/src/test/java/seedu/address/model/tag/TutorialTagTest.java
@@ -0,0 +1,21 @@
+package seedu.address.model.tag;
+
+import org.junit.jupiter.api.Test;
+
+class TutorialTagTest {
+
+ @Test
+ public void equals_theSameTag_success() {
+ String validTagName = "test";
+ TutorialTag tag1 = new TutorialTag(validTagName, TagStatus.AVAILABLE);
+ assert(tag1.isSameTutorialTag(tag1));
+ }
+
+ @Test
+ public void equals_tagWithTheSameTagName_success() {
+ String validTagName = "test";
+ TutorialTag tag1 = new TutorialTag(validTagName, TagStatus.AVAILABLE);
+ TutorialTag tag2 = new TutorialTag(validTagName, TagStatus.AVAILABLE);
+ assert(tag1.isSameTutorialTag(tag2));
+ }
+}
diff --git a/src/test/java/seedu/address/model/util/SampleDataUtilTest.java b/src/test/java/seedu/address/model/util/SampleDataUtilTest.java
new file mode 100644
index 00000000000..6b08ecec5a5
--- /dev/null
+++ b/src/test/java/seedu/address/model/util/SampleDataUtilTest.java
@@ -0,0 +1,70 @@
+package seedu.address.model.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.person.Person;
+import seedu.address.testutil.PersonBuilder;
+public class SampleDataUtilTest {
+
+ @Test
+ public void testGetSamplePersons() {
+ // Get the sample persons array
+ Person[] samplePersons = SampleDataUtil.getSamplePersons();
+
+ // Check if the array is not null
+ assertNotNull(samplePersons);
+
+ // Check if the length of the array is correct
+ assertEquals(5, samplePersons.length);
+
+ // Test individual properties of each person object in the array
+ assertPersonEquals(new PersonBuilder().withName("Bernice Yu")
+ .withId("A9128392K")
+ .withPhone("99272758")
+ .withEmail("berniceyu@example.com")
+ .withTags()
+ .build(), samplePersons[0]);
+
+ assertPersonEquals(new PersonBuilder().withName("Charlotte Oliveiro")
+ .withId("A2222222P")
+ .withPhone("93210283")
+ .withEmail("charlotte@example.com")
+ .withTags()
+ .build(), samplePersons[1]);
+
+ assertPersonEquals(new PersonBuilder().withName("David Li")
+ .withId("A9128392Z")
+ .withPhone("91031282")
+ .withEmail("lidavid@example.com")
+ .withTags()
+ .build(), samplePersons[2]);
+
+ assertPersonEquals(new PersonBuilder().withName("Irfan Ibrahim")
+ .withId("B0198266Z")
+ .withPhone("92492021")
+ .withEmail("irfan@example.com")
+ .withTags()
+ .build(), samplePersons[3]);
+
+ assertPersonEquals(new PersonBuilder().withName("Roy Balakrishnan")
+ .withId("B0000666C")
+ .withPhone("92624417")
+ .withEmail("royb@example.com")
+ .withTags()
+ .build(), samplePersons[4]);
+ }
+
+ // Helper method to assert properties of a person
+ private void assertPersonEquals(Person expectedPerson, Person actualPerson) {
+ // Check individual properties of the person objects
+ assertEquals(expectedPerson.getName().toString(), actualPerson.getName().toString());
+ assertEquals(expectedPerson.getId().toString(), actualPerson.getId().toString());
+ assertEquals(expectedPerson.getPhone().toString(), actualPerson.getPhone().toString());
+ assertEquals(expectedPerson.getEmail().toString(), actualPerson.getEmail().toString());
+ assertTrue(actualPerson.getTags().containsAll(expectedPerson.getTags()));
+ }
+}
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
index 83b11331cdb..be028095b39 100644
--- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
+++ b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
@@ -12,22 +12,25 @@
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.Id;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TagType;
public class JsonAdaptedPersonTest {
+ private static final String TYPE = "stu";
private static final String INVALID_NAME = "R@chel";
+ private static final String INVALID_ID = "BBBBCCCCCC";
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_ID = BENSON.getId().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());
@@ -41,69 +44,74 @@ public void toModelType_validPersonDetails_returnsPerson() throws Exception {
@Test
public void toModelType_invalidName_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ new JsonAdaptedPerson(TYPE, INVALID_NAME, VALID_ID, VALID_PHONE, VALID_EMAIL,
+ 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);
+ JsonAdaptedPerson person = new JsonAdaptedPerson(TYPE, null, VALID_ID, VALID_PHONE, VALID_EMAIL,
+ 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() {
+ public void toModelType_invalidId_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
- String expectedMessage = Phone.MESSAGE_CONSTRAINTS;
+ new JsonAdaptedPerson(TYPE, VALID_NAME, INVALID_ID, VALID_PHONE, VALID_EMAIL, VALID_TAGS);
+ String expectedMessage = Id.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());
+ public void toModelType_nullId_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(TYPE, VALID_NAME, null, VALID_PHONE, VALID_EMAIL,
+ VALID_TAGS);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Id.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
- public void toModelType_invalidEmail_throwsIllegalValueException() {
+ public void toModelType_invalidPhone_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
- String expectedMessage = Email.MESSAGE_CONSTRAINTS;
+ new JsonAdaptedPerson(TYPE, VALID_NAME, VALID_ID, INVALID_PHONE, VALID_EMAIL, VALID_TAGS);
+ String expectedMessage = Phone.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());
+ public void toModelType_nullPhone_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(TYPE, VALID_NAME, VALID_ID, null, VALID_EMAIL,
+ VALID_TAGS);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
- public void toModelType_invalidAddress_throwsIllegalValueException() {
+ public void toModelType_invalidEmail_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS);
- String expectedMessage = Address.MESSAGE_CONSTRAINTS;
+ new JsonAdaptedPerson(TYPE, VALID_NAME, VALID_ID, VALID_PHONE, INVALID_EMAIL, VALID_TAGS);
+ String expectedMessage = Email.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());
+ public void toModelType_nullEmail_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(TYPE, VALID_NAME, VALID_ID,
+ VALID_PHONE, null, VALID_TAGS);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.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));
+ invalidTags.add(new JsonAdaptedTag(INVALID_TAG, TagStatus.INCOMPLETE_GOOD, TagType.DEFAULT_TYPE));
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags);
+ new JsonAdaptedPerson(TYPE, VALID_NAME, VALID_ID, VALID_PHONE, VALID_EMAIL, invalidTags);
assertThrows(IllegalValueException.class, person::toModelType);
}
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedTagTest.java b/src/test/java/seedu/address/storage/JsonAdaptedTagTest.java
new file mode 100644
index 00000000000..39006d8522d
--- /dev/null
+++ b/src/test/java/seedu/address/storage/JsonAdaptedTagTest.java
@@ -0,0 +1,22 @@
+package seedu.address.storage;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.testutil.TypicalTutorialTag.WED10;
+
+import org.junit.jupiter.api.Test;
+
+
+class JsonAdaptedTagTest {
+
+ @Test
+ public void getTagName() {
+ JsonAdaptedTutorialTag wed10 = new JsonAdaptedTutorialTag(WED10.getTagName());
+ assertEquals(wed10.getTagName(), WED10.getTagName());
+ }
+
+ @Test
+ public void createJsonAdaptedTutorialTagFromTutorialTag() {
+ JsonAdaptedTutorialTag wed10 = new JsonAdaptedTutorialTag(WED10);
+ assertEquals(wed10.getTagName(), WED10.getTagName());
+ }
+}
diff --git a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java
index 188c9058d20..adb81251a17 100644
--- a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java
+++ b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java
@@ -15,10 +15,16 @@
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 TEST_DATA_FOLDER = Paths.get("src",
+ "test", "data", "JsonSerializableAddressBookTest");
+ private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve(
+ "typicalPersonsAddressBook.json");
private static final Path INVALID_PERSON_FILE = TEST_DATA_FOLDER.resolve("invalidPersonAddressBook.json");
private static final Path DUPLICATE_PERSON_FILE = TEST_DATA_FOLDER.resolve("duplicatePersonAddressBook.json");
+ private static final Path DUPLICATE_TUTORIALTAG_FILE = TEST_DATA_FOLDER.resolve(
+ "duplicateTutorialTagsAddressBook.json");
+ private static final Path INVALID_TUTORIALTAG_FILE = TEST_DATA_FOLDER.resolve(
+ "invalidTutorialTagsAddressBook.json");
@Test
public void toModelType_typicalPersonsFile_success() throws Exception {
@@ -44,4 +50,19 @@ public void toModelType_duplicatePersons_throwsIllegalValueException() throws Ex
dataFromFile::toModelType);
}
+ @Test
+ public void toModelType_duplicateTutorialTags_throwsIllegalValueException() throws Exception {
+ JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_TUTORIALTAG_FILE,
+ JsonSerializableAddressBook.class).get();
+ assertThrows(IllegalValueException.class, JsonSerializableAddressBook.MESSAGE_DUPLICATE_TUTORIALTAG,
+ dataFromFile::toModelType);
+ }
+
+ @Test
+ public void toModelType_invalidTutorialTagFile_throwsIllegalValueException() throws Exception {
+ JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(INVALID_TUTORIALTAG_FILE,
+ JsonSerializableAddressBook.class).get();
+ assertThrows(IllegalValueException.class, dataFromFile::toModelType);
+ }
+
}
diff --git a/src/test/java/seedu/address/testutil/AddressBookBuilder.java b/src/test/java/seedu/address/testutil/AddressBookBuilder.java
index d53799fd110..d60532a8c09 100644
--- a/src/test/java/seedu/address/testutil/AddressBookBuilder.java
+++ b/src/test/java/seedu/address/testutil/AddressBookBuilder.java
@@ -2,6 +2,7 @@
import seedu.address.model.AddressBook;
import seedu.address.model.person.Person;
+import seedu.address.model.tag.TutorialTag;
/**
* A utility class to help with building Addressbook objects.
@@ -28,6 +29,14 @@ public AddressBookBuilder withPerson(Person person) {
return this;
}
+ /**
+ * Adds a new {@code TutorialTag} to the {@code AddressBook} that we are building.
+ */
+ public AddressBookBuilder withTutorialTag(TutorialTag tutorialTag) {
+ addressBook.addTutorialTag(tutorialTag);
+ 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
index 4584bd5044e..01014ee2120 100644
--- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
+++ b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
@@ -1,16 +1,10 @@
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.
@@ -35,8 +29,6 @@ public EditPersonDescriptorBuilder(Person person) {
descriptor.setName(person.getName());
descriptor.setPhone(person.getPhone());
descriptor.setEmail(person.getEmail());
- descriptor.setAddress(person.getAddress());
- descriptor.setTags(person.getTags());
}
/**
@@ -63,24 +55,6 @@ public EditPersonDescriptorBuilder withEmail(String 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
index 6be381d39ba..041daf2b79c 100644
--- a/src/test/java/seedu/address/testutil/PersonBuilder.java
+++ b/src/test/java/seedu/address/testutil/PersonBuilder.java
@@ -3,12 +3,14 @@
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.Id;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
+import seedu.address.model.person.PersonType;
import seedu.address.model.person.Phone;
import seedu.address.model.tag.Tag;
+import seedu.address.model.tag.TagStatus;
import seedu.address.model.util.SampleDataUtil;
/**
@@ -16,25 +18,27 @@
*/
public class PersonBuilder {
+ public static final String DEFAULT_TYPE = "stu";
public static final String DEFAULT_NAME = "Amy Bee";
+ public static final String DEFAULT_ID = "A1234567Z";
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 PersonType type;
private Name name;
+ private Id id;
private Phone phone;
private Email email;
- private Address address;
private Set tags;
/**
* Creates a {@code PersonBuilder} with the default details.
*/
public PersonBuilder() {
+ type = PersonType.getPersonType(DEFAULT_TYPE);
name = new Name(DEFAULT_NAME);
+ id = new Id(DEFAULT_ID);
phone = new Phone(DEFAULT_PHONE);
email = new Email(DEFAULT_EMAIL);
- address = new Address(DEFAULT_ADDRESS);
tags = new HashSet<>();
}
@@ -42,37 +46,45 @@ public PersonBuilder() {
* Initializes the PersonBuilder with the data of {@code personToCopy}.
*/
public PersonBuilder(Person personToCopy) {
+ type = personToCopy.getType();
name = personToCopy.getName();
+ id = personToCopy.getId();
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.
+ * Sets the {@code PersonType} of the {@code Person} that we are building.
*/
- public PersonBuilder withName(String name) {
- this.name = new Name(name);
+ public PersonBuilder withType(String type) {
+ this.type = PersonType.getPersonType(type);
return this;
}
/**
- * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building.
+ * Sets the {@code Id} of the {@code Person} that we are building.
*/
- public PersonBuilder withTags(String ... tags) {
- this.tags = SampleDataUtil.getTagSet(tags);
+ public PersonBuilder withId(String id) {
+ this.id = new Id(id);
return this;
}
/**
- * Sets the {@code Address} of the {@code Person} that we are building.
+ * Sets the {@code Name} of the {@code Person} that we are building.
*/
- public PersonBuilder withAddress(String address) {
- this.address = new Address(address);
+ 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 Phone} of the {@code Person} that we are building.
*/
@@ -89,8 +101,26 @@ public PersonBuilder withEmail(String email) {
return this;
}
- public Person build() {
- return new Person(name, phone, email, address, tags);
+ /**
+ * Sets the {@code Tag} of the {@code Person} that we are building.
+ */
+ public PersonBuilder addTag(String tagName, TagStatus tagStatus) {
+ this.tags = Tag.updateTagsWithNewTag(new HashSet<>(tags), tagName, tagStatus);
+ return this;
}
+ /**
+ * Removes specified tag from the {@code Tag} of the {@code Person} that we are building.
+ */
+ public PersonBuilder removeTag(String tagName) {
+ this.tags = Tag.removeTagFromTagSet(new HashSet<>(tags), tagName);
+ return this;
+ }
+
+ /**
+ * Builds the {@code Person}.
+ */
+ public Person build() {
+ return Person.of(type, name, id, phone, email, tags);
+ }
}
diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java
index 90849945183..8d608f23e18 100644
--- a/src/test/java/seedu/address/testutil/PersonUtil.java
+++ b/src/test/java/seedu/address/testutil/PersonUtil.java
@@ -1,17 +1,14 @@
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_ID;
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.
@@ -30,12 +27,13 @@ public static String getAddCommand(Person person) {
*/
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 + " ");
+ sb.append(person.getType().toString() + " ");
+ sb.append(PREFIX_NAME + " " + person.getName().fullName + " ");
+ sb.append(PREFIX_ID + " " + person.getId().value + " ");
+ sb.append(PREFIX_PHONE + " " + person.getPhone().value + " ");
+ sb.append(PREFIX_EMAIL + " " + person.getEmail().value + " ");
person.getTags().stream().forEach(
- s -> sb.append(PREFIX_TAG + s.tagName + " ")
+ s -> sb.append(PREFIX_TAG + " " + s.getTagName() + " ")
);
return sb.toString();
}
@@ -45,18 +43,9 @@ public static String getPersonDetails(Person person) {
*/
public static String getEditPersonDescriptorDetails(EditPersonDescriptor descriptor) {
StringBuilder sb = new StringBuilder();
- descriptor.getName().ifPresent(name -> sb.append(PREFIX_NAME).append(name.fullName).append(" "));
- descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" "));
- descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" "));
- descriptor.getAddress().ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" "));
- 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(" "));
- }
- }
+ descriptor.getName().ifPresent(name -> sb.append(PREFIX_NAME).append(" ").append(name.fullName).append(" "));
+ descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(" ").append(phone.value).append(" "));
+ descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(" ").append(email.value).append(" "));
return sb.toString();
}
}
diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/seedu/address/testutil/TypicalIndexes.java
index 1e613937657..90eb1ebf7b7 100644
--- a/src/test/java/seedu/address/testutil/TypicalIndexes.java
+++ b/src/test/java/seedu/address/testutil/TypicalIndexes.java
@@ -1,5 +1,9 @@
package seedu.address.testutil;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
import seedu.address.commons.core.index.Index;
/**
@@ -9,4 +13,10 @@ 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);
+ public static final Set INDEX_1_SET = new HashSet<>(List.of(INDEX_FIRST_PERSON));
+ public static final Set INDEX_2_SET = new HashSet<>(List.of(INDEX_SECOND_PERSON));
+ public static final Set INDEX_3_SET = new HashSet<>(List.of(INDEX_THIRD_PERSON));
+ public static final Set INDEX_1_2_SET = new HashSet<>(List.of(INDEX_FIRST_PERSON, INDEX_SECOND_PERSON));
+ public static final Set INDEX_1_2_3_SET = new HashSet<>(List.of(INDEX_FIRST_PERSON, INDEX_SECOND_PERSON,
+ INDEX_THIRD_PERSON));
}
diff --git a/src/test/java/seedu/address/testutil/TypicalModel.java b/src/test/java/seedu/address/testutil/TypicalModel.java
new file mode 100644
index 00000000000..10ee55fece0
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/TypicalModel.java
@@ -0,0 +1,20 @@
+package seedu.address.testutil;
+
+import static seedu.address.testutil.TypicalPersons.JANE;
+import static seedu.address.testutil.TypicalPersons.JOHN;
+import static seedu.address.testutil.TypicalTutorialTag.THU10;
+import static seedu.address.testutil.TypicalTutorialTag.WED10;
+
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+
+/**
+ * A utility class containing a {@code Model} object to be used in tests.
+ */
+public class TypicalModel {
+ public static final AddressBook AB = new AddressBookBuilder().withPerson(JOHN).withPerson(JANE)
+ .withTutorialTag(WED10).withTutorialTag(THU10).build();
+ public static final Model MODEL = new ModelManager(AB, new UserPrefs());
+}
diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java
index fec76fb7129..b93ee03f530 100644
--- a/src/test/java/seedu/address/testutil/TypicalPersons.java
+++ b/src/test/java/seedu/address/testutil/TypicalPersons.java
@@ -1,15 +1,17 @@
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_ID_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_ID_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_FRIENDS;
import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TYPE_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_TYPE_BOB;
import java.util.ArrayList;
import java.util.Arrays;
@@ -17,44 +19,62 @@
import seedu.address.model.AddressBook;
import seedu.address.model.person.Person;
+import seedu.address.model.tag.TagStatus;
/**
* 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")
+ public static final Person ALICE = new PersonBuilder().withType("stu").withName("Alice Pauline").withId("A0111111A")
+ .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")
+ public static final Person BENSON = new PersonBuilder().withType("stu").withName("Benson Meier").withId("A0111111C")
.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();
+ public static final Person CARL = new PersonBuilder().withType("stu").withName("Carl Kurz").withId("A2222222D")
+ .withPhone("95352563").withEmail("heinz@example.com").build();
+ public static final Person DANIEL = new PersonBuilder().withType("stu").withName("Daniel Meier")
+ .withId("B3334444E").withPhone("87652533").withEmail("cornelia@example.com")
+ .withTags("friends").build();
+ public static final Person ELLE = new PersonBuilder().withType("stu").withName("Elle Meyer")
+ .withId("B3334444F").withPhone("9482224").withEmail("werner@example.com")
+ .build();
+ public static final Person FIONA = new PersonBuilder().withType("ta").withName("Fiona Kunz")
+ .withId("B5678012F").withPhone("9482427").withEmail("lydia@example.com")
+ .build();
+ public static final Person GEORGE = new PersonBuilder().withType("ta").withName("George Best")
+ .withId("A0000000X").withPhone("9482442")
+ .withEmail("anna@example.com").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();
+ public static final Person HOON = new PersonBuilder().withType("stu").withName("Hoon Meier")
+ .withId("A0123456W").withPhone("8482424").withEmail("stefan@example.com")
+ .build();
+ public static final Person IDA = new PersonBuilder().withType("ta").withName("Ida Mueller")
+ .withId("P9876543N").withPhone("8482131").withEmail("hans@example.com")
+ .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)
+ public static final Person JANE = new PersonBuilder().withType("stu").withName("Jane Doe").withId("A0111111B")
+ .withPhone("98765432").withEmail("jane@example.com").addTag("W10", TagStatus.getTagStatus("as"))
+ .addTag("Assignment1", TagStatus.getTagStatus("cg")).build();
+
+ public static final Person JOHN = new PersonBuilder().withType("ta").withName("John Doe").withId("A0111111D")
+ .withPhone("98765432").withEmail("john@example.com").addTag("TUES08", TagStatus.getTagStatus("av"))
.build();
+ // Manually added - Person's details found in {@code CommandTestUtil}
+ public static final Person AMY =
+
+ new PersonBuilder().withType(VALID_TYPE_AMY).withName(VALID_NAME_AMY).withId(VALID_ID_AMY)
+ .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY)
+ .withTags(VALID_TAG_FRIENDS).build();
+ public static final Person BOB =
+ new PersonBuilder().withType(VALID_TYPE_BOB).withName(VALID_NAME_BOB).withId(VALID_ID_BOB)
+ .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB)
+ .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIENDS).build();
+
public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER
private TypicalPersons() {} // prevents instantiation
diff --git a/src/test/java/seedu/address/testutil/TypicalTutorialTag.java b/src/test/java/seedu/address/testutil/TypicalTutorialTag.java
new file mode 100644
index 00000000000..0920f369fa3
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/TypicalTutorialTag.java
@@ -0,0 +1,14 @@
+package seedu.address.testutil;
+
+import seedu.address.model.tag.TagStatus;
+import seedu.address.model.tag.TutorialTag;
+
+/**
+ * A utility class containing {@code TutorialTag} objects to be used in tests.
+ */
+public class TypicalTutorialTag {
+
+ public static final TutorialTag TUES08 = new TutorialTag("TUES08", TagStatus.ASSIGNED);
+ public static final TutorialTag WED10 = new TutorialTag("WED10", TagStatus.AVAILABLE);
+ public static final TutorialTag THU10 = new TutorialTag("THU10", TagStatus.ASSIGNED);
+}
diff --git a/src/test/java/seedu/address/testutil/TypicalValidTutorialTagList.java b/src/test/java/seedu/address/testutil/TypicalValidTutorialTagList.java
new file mode 100644
index 00000000000..79bf1b7cd97
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/TypicalValidTutorialTagList.java
@@ -0,0 +1,28 @@
+package seedu.address.testutil;
+
+import static seedu.address.testutil.TypicalTutorialTag.THU10;
+import static seedu.address.testutil.TypicalTutorialTag.TUES08;
+import static seedu.address.testutil.TypicalTutorialTag.WED10;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.tag.TutorialTag;
+
+/**
+ * A utility class containing a list of {@code TutorialTag} objects to be used in tests.
+ */
+public class TypicalValidTutorialTagList {
+
+ public static final ObservableList VALID_TUTORIAL_TAG_LIST = initaliseValidTutorialTagList();
+ /**
+ * Returns a list of valid {@code TutorialTag} objects.
+ */
+ public static ObservableList initaliseValidTutorialTagList() {
+ ObservableList validTutorialTagList = FXCollections.observableArrayList();
+ validTutorialTagList.add(WED10);
+ validTutorialTagList.add(THU10);
+ validTutorialTagList.add(TUES08);
+ return validTutorialTagList;
+ }
+
+}
diff --git a/src/test/java/seedu/address/ui/PersonCardTest.java b/src/test/java/seedu/address/ui/PersonCardTest.java
new file mode 100644
index 00000000000..53214079c99
--- /dev/null
+++ b/src/test/java/seedu/address/ui/PersonCardTest.java
@@ -0,0 +1,4 @@
+package seedu.address.ui;
+
+class PersonCardTest {
+}