getValue(Prefix prefix) {
}
```
-This appears to be what we need to get a String of the remark. But what about the Index? Let's take a quick peek at existing `Command` that uses an index to see how it is done.
+This appears to be what we need to get a String of the remark. But what about the Index? Let's take a quick peek at
+existing `Command` that uses an index to see how it is done.
**`DeleteCommandParser.java`:**
@@ -188,7 +206,8 @@ return new DeleteCommand(index);
There appears to be another utility class that obtains an `Index` from the input provided by the user.
-Now that we have the know-how to extract the data that we need from the user’s input, we can parse the user command and create a new instance of `RemarkCommand`, as given below.
+Now that we have the know-how to extract the data that we need from the user’s input, we can parse the user command and
+create a new instance of `RemarkCommand`, as given below.
**`RemarkCommandParser.java`:**
@@ -223,24 +242,33 @@ If you are stuck, check out the sample
## Add `Remark` to the model
-Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the in-memory storage of person data. We achieve that by working with the `Person` model. Each field in a Person is implemented as a separate class (e.g. a `Name` object represents the person’s name). That means we should add a `Remark` class so that we can use a `Remark` object to represent a remark given to a person.
+Now that we have all the information that we need, let’s lay the groundwork for propagating the remarks added into the
+in-memory storage of developer data. We achieve that by working with the `Person` model. Each field in a Person is
+implemented as a separate class (e.g. a `Name` object represents the developer’s name). That means we should add
+a `Remark` class so that we can use a `Remark` object to represent a remark given to a developer.
### Add a new `Remark` class
-Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code.
+Create a new `Remark` in `seedu.address.model.developer`. Since a `Remark` is a field that is similar to `Address`, we
+can reuse a significant bit of code.
-A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input
+A copy-paste and search-replace later, you should have something
+like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece).
+Note how `Remark` has no constrains and thus does not require input
validation.
### Make use of `Remark`
-Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` class instead of plain `String`. These should be relatively simple changes.
+Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` class instead of plain `String`. These
+should be relatively simple changes.
## Add a placeholder element for remark to the UI
-Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person.
+Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each
+developer.
-Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
+Simply add the following
+to [`seedu.address.ui.DeveloperCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688).
**`PersonCard.java`:**
@@ -249,10 +277,11 @@ Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/s
private Label remark;
```
+`@FXML` is an annotation that marks a private or protected field and makes it accessible to FXML. It might sound like
+Greek to you right now, don’t worry — we will get back to it later.
-`@FXML` is an annotation that marks a private or protected field and makes it accessible to FXML. It might sound like Greek to you right now, don’t worry — we will get back to it later.
-
-Then insert the following into [`main/resources/view/PersonListCard.fxml`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-d44c4f51c24f6253c277a2bb9bc440b8064d9c15ad7cb7ceda280bca032efce9).
+Then insert the following
+into [`main/resources/view/PersonListCard.fxml`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-d44c4f51c24f6253c277a2bb9bc440b8064d9c15ad7cb7ceda280bca032efce9).
**`PersonListCard.fxml`:**
@@ -270,11 +299,13 @@ Since `PersonCard` displays data from a `Person`, we need to update `Person` to
### Modify `Person`
-We change the constructor of `Person` to take a `Remark`. We will also need to define new fields and accessors accordingly to store our new addition.
+We change the constructor of `Person` to take a `Remark`. We will also need to define new fields and accessors
+accordingly to store our new addition.
### Update other usages of `Person`
-Unfortunately, a change to `Person` will cause other commands to break, you will have to modify these commands to use the updated `Person`!
+Unfortunately, a change to `Person` will cause other commands to break, you will have to modify these commands to use
+the updated `Person`!
@@ -282,18 +313,20 @@ Unfortunately, a change to `Person` will cause other commands to break, you will
-Refer to [this commit](https://github.com/se-edu/addressbook-level3/commit/ce998c37e65b92d35c91d28c7822cd139c2c0a5c) and check that you have got everything in order!
-
+Refer to [this commit](https://github.com/se-edu/addressbook-level3/commit/ce998c37e65b92d35c91d28c7822cd139c2c0a5c) and
+check that you have got everything in order!
## Updating Storage
-AddressBook stores data by serializing `JsonAdaptedPerson` into `json` with the help of an external library — Jackson. Let’s update `JsonAdaptedPerson` to work with our new `Person`!
+AddressBook stores data by serializing `JsonAdaptedPerson` into `json` with the help of an external library — Jackson.
+Let’s update `JsonAdaptedPerson` to work with our new `Person`!
While the changes to code may be minimal, the test data will have to be updated as well.
-:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
+:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not
+doing so will cause AddressBook to default to an empty address book!
@@ -304,14 +337,15 @@ to see what the changes entail.
Now that we have finalized the `Person` class and its dependencies, we can now bind the `Remark` field to the UI.
-Just add [this one line of code!](https://github.com/se-edu/addressbook-level3/commit/5b98fee11b6b3f5749b6b943c4f3bd3aa049b692)
+Just
+add [this one line of code!](https://github.com/se-edu/addressbook-level3/commit/5b98fee11b6b3f5749b6b943c4f3bd3aa049b692)
**`PersonCard.java`:**
``` java
-public PersonCard(Person person, int displayedIndex) {
+public PersonCard(Person developer, int displayedIndex) {
//...
- remark.setText(person.getRemark().value);
+ remark.setText(developer.getRemark().value);
}
```
@@ -319,11 +353,14 @@ public PersonCard(Person person, int displayedIndex) {
## Putting everything together
-After the previous step, we notice a peculiar regression — we went from displaying something to nothing at all. However, this is expected behavior as we are yet to update the `RemarkCommand` to make use of the code we've been adding in the last few steps.
+After the previous step, we notice a peculiar regression — we went from displaying something to nothing at all. However,
+this is expected behavior as we are yet to update the `RemarkCommand` to make use of the code we've been adding in the
+last few steps.
### Update `RemarkCommand` and `RemarkCommandParser`
-In this last step, we modify `RemarkCommand#execute()` to change the `Remark` of a `Person`. Since all fields in a `Person` are immutable, we create a new instance of a `Person` with the values that we want and
+In this last step, we modify `RemarkCommand#execute()` to change the `Remark` of a `Person`. Since all fields in
+a `Person` are immutable, we create a new instance of a `Person` with the values that we want and
save it with `Model#setPerson()`.
**`RemarkCommand.java`:**
@@ -341,25 +378,25 @@ save it with `Model#setPerson()`.
throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
}
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = new Person(
- personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(),
- personToEdit.getAddress(), remark, personToEdit.getTags());
+ Person developerToEdit = lastShownList.get(index.getZeroBased());
+ Person editedDeveloper = new Person(
+ developerToEdit.getName(), developerToEdit.getPhone(), developerToEdit.getEmail(),
+ developerToEdit.getAddress(), remark, developerToEdit.getTags());
- model.setPerson(personToEdit, editedPerson);
+ model.setPerson(developerToEdit, editedDeveloper);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(generateSuccessMessage(editedPerson));
+ return new CommandResult(generateSuccessMessage(editedDeveloper));
}
/**
* Generates a command execution success message based on whether
* the remark is added to or removed from
- * {@code personToEdit}.
+ * {@code developerToEdit}.
*/
- private String generateSuccessMessage(Person personToEdit) {
+ private String generateSuccessMessage(Person developerToEdit) {
String message = !remark.value.isEmpty() ? MESSAGE_ADD_REMARK_SUCCESS : MESSAGE_DELETE_REMARK_SUCCESS;
- return String.format(message, personToEdit);
+ return String.format(message, developerToEdit);
}
```
@@ -367,11 +404,16 @@ save it with `Model#setPerson()`.
## Writing tests
-Tests are crucial to ensuring that bugs don’t slip into the codebase unnoticed. This is especially true for large code bases where a change might lead to unintended behavior.
+Tests are crucial to ensuring that bugs don’t slip into the codebase unnoticed. This is especially true for large code
+bases where a change might lead to unintended behavior.
Let’s verify the correctness of our code by writing some tests!
-Of course you can simply add the test cases manually, like you've been doing all along this tutorial. The result would be like the test cases in [here](https://github.com/se-edu/addressbook-level3/commit/fac8f3fd855d55831ca0cc73313b5943d49d4d6e#diff-ff58f7c10338b34f76645df49b71ecb2bafaf7611b20e7ff59ebc98475538a01). Alternatively, you can get the help of IntelliJ to generate the skeletons of the test cases, as explained in the next section.
+Of course you can simply add the test cases manually, like you've been doing all along this tutorial. The result would
+be like the test cases
+in [here](https://github.com/se-edu/addressbook-level3/commit/fac8f3fd855d55831ca0cc73313b5943d49d4d6e#diff-ff58f7c10338b34f76645df49b71ecb2bafaf7611b20e7ff59ebc98475538a01).
+Alternatively, you can get the help of IntelliJ to generate the skeletons of the test cases, as explained in the next
+section.
### Automatically generating tests
@@ -380,7 +422,8 @@ The goal is to write effective and efficient tests to ensure that `RemarkCommand
The convention for test names is `methodName_testScenario_expectedResult`. An example would be
`execute_filteredList_success`.
-Let’s create a test for `RemarkCommand#execute()` to test that adding a remark works. On `IntelliJ IDEA` you can bring up the context menu and choose to `Go To` \> `Test` or use the appropriate keyboard shortcut.
+Let’s create a test for `RemarkCommand#execute()` to test that adding a remark works. On `IntelliJ IDEA` you can bring
+up the context menu and choose to `Go To` \> `Test` or use the appropriate keyboard shortcut.
![Using the context menu to jump to tests](../images/add-remark/ContextMenu.png)
@@ -390,9 +433,12 @@ Then, create a test for the `execute` method.
Following convention, let’s change the name of the generated method to `execute_addRemarkUnfilteredList_success`.
-Let’s use the utility functions provided in `CommandTestUtil`. The functions ensure that commands produce the expected `CommandResult` and output the correct message. In this case, `CommandTestUtil#assertCommandSuccess` is the best fit as we are testing that a `RemarkCommand` will successfully add a `Remark`.
+Let’s use the utility functions provided in `CommandTestUtil`. The functions ensure that commands produce the
+expected `CommandResult` and output the correct message. In this case, `CommandTestUtil#assertCommandSuccess` is the
+best fit as we are testing that a `RemarkCommand` will successfully add a `Remark`.
-You should end up with a test that looks something like [this](https://github.com/se-edu/addressbook-level3/commit/fac8f3fd855d55831ca0cc73313b5943d49d4d6e#diff-ff58f7c10338b34f76645df49b71ecb2bafaf7611b20e7ff59ebc98475538a01R36-R49).
+You should end up with a test that looks something
+like [this](https://github.com/se-edu/addressbook-level3/commit/fac8f3fd855d55831ca0cc73313b5943d49d4d6e#diff-ff58f7c10338b34f76645df49b71ecb2bafaf7611b20e7ff59ebc98475538a01R36-R49).
## Conclusion
diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md
index f29169bc924..e4b39e0e39f 100644
--- a/docs/tutorials/RemovingFields.md
+++ b/docs/tutorials/RemovingFields.md
@@ -5,39 +5,52 @@ title: "Tutorial: Removing Fields"
> Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
>
-> — Antoine de Saint-Exupery
+> — Antoine de Saint-Exupery
When working on an existing code base, you will most likely find that some features that are no longer necessary.
-This tutorial aims to give you some practice on such a code 'removal' activity by removing the `address` field from `Person` class.
+This tutorial aims to give you some practice on such a code 'removal' activity by removing the `address` field
+from `Person` class.
-**If you have done the [Add `remark` command tutorial](AddRemark.html) already**, you should know where the code had to be updated to add the field `remark`. From that experience, you can deduce where the code needs to be changed to _remove_ that field too. The removing of the `address` field can be done similarly.
+**If you have done the [Add `remark` command tutorial](AddRemark.html) already**, you should know where the code had to
+be updated to add the field `remark`. From that experience, you can deduce where the code needs to be changed to
+_remove_ that field too. The removing of the `address` field can be done similarly.
-However, if you have no such prior knowledge, removing a field can take a quite a bit of detective work. This tutorial takes you through that process. **At least have a read even if you don't actually do the steps yourself.**
+However, if you have no such prior knowledge, removing a field can take a quite a bit of detective work. This tutorial
+takes you through that process. **At least have a read even if you don't actually do the steps yourself.**
-
* Table of Contents
-{:toc}
+ {:toc}
## Safely deleting `Address`
-IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a removal easily. Let’s try to use it as much as we can.
+IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a removal easily. Let’s try to use it as
+much as we can.
### Assisted refactoring
-The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu.
-* :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences`
+The `address` field in `Person` is actually an instance of the `seedu.address.model.developer.Address` class. Since
+removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to
+see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address`
+class and select `Refactor` \> `Safe Delete` through the menu.
+
+* :bulb: To make things simpler, you can unselect the options `Search in comments and strings`
+ and `Search for text occurrences`
![Usages detected](../images/remove/UnsafeDelete.png)
-Choose to `View Usages` and you should be presented with a list of `Safe Delete Conflicts`. These conflicts describe locations in which the `Address` class is used.
+Choose to `View Usages` and you should be presented with a list of `Safe Delete Conflicts`. These conflicts describe
+locations in which the `Address` class is used.
![List of conflicts](../images/remove/SafeDeleteConflicts.png)
-Remove usages of `Address` by performing `Safe Delete`s on each entry i.e., double-click on the entry (which takes you to the code in concern, right-click on that entity, and choose `Refactor` -> `Safe delete` as before). You will need to exercise discretion when removing usages of `Address`. Functions like `ParserUtil#parseAddress()` can be safely removed but its usages must be removed as well. Other usages like in `EditPersonDescriptor` may require more careful inspection.
+Remove usages of `Address` by performing `Safe Delete`s on each entry i.e., double-click on the entry (which takes you
+to the code in concern, right-click on that entity, and choose `Refactor` -> `Safe delete` as before). You will need to
+exercise discretion when removing usages of `Address`. Functions like `ParserUtil#parseAddress()` can be safely removed
+but its usages must be removed as well. Other usages like in `EditPersonDescriptor` may require more careful inspection.
Let’s try removing references to `Address` in `EditPersonDescriptor`.
@@ -52,7 +65,8 @@ Let’s try removing references to `Address` in `EditPersonDescriptor`.
- :bulb: **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Person` class will require you to modify its constructor.
+ :bulb: **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing
+ the `address` field from the `Person` class will require you to modify its constructor.
1. Repeat the steps for the remaining usages of `Address`
@@ -61,13 +75,17 @@ After you are done, verify that the application still works by compiling and run
### Manual refactoring
-Unfortunately, there are usages of `Address` that IntelliJ IDEA cannot identify. You can find them by searching for instances of the word `address` in your code (`Edit` \> `Find` \> `Find in path`).
+Unfortunately, there are usages of `Address` that IntelliJ IDEA cannot identify. You can find them by searching for
+instances of the word `address` in your code (`Edit` \> `Find` \> `Find in path`).
-Places of interest to look out for would be resources used by the application. `main/resources` contains images and `fxml` files used by the application and `test/resources` contains test data. For example, there is a `$address` in each `PersonCard` that has not been removed nor identified.
+Places of interest to look out for would be resources used by the application. `main/resources` contains images
+and `fxml` files used by the application and `test/resources` contains test data. For example, there is a `$address` in
+each `PersonCard` that has not been removed nor identified.
![$address](../images/remove/$address.png)
-A quick look at the `PersonCard` class and its `fxml` file quickly reveals why it slipped past the automated refactoring.
+A quick look at the `PersonCard` class and its `fxml` file quickly reveals why it slipped past the automated
+refactoring.
**`PersonCard.java`**
@@ -88,24 +106,29 @@ private Label address;
...
```
-After removing the `Label`, we can proceed to formally test our code. If everything went well, you should have most of your tests pass. Fix any remaining errors until the tests all pass.
+After removing the `Label`, we can proceed to formally test our code. If everything went well, you should have most of
+your tests pass. Fix any remaining errors until the tests all pass.
## Tidying up
-At this point, your application is working as intended and all your tests are passing. What’s left to do is to clean up references to `Address` in test data and documentation.
+At this point, your application is working as intended and all your tests are passing. What’s left to do is to clean up
+references to `Address` in test data and documentation.
-In `src/test/data/`, data meant for testing purposes are stored. While keeping the `address` field in the json files does not cause the tests to fail, it is not good practice to let cruft from old features accumulate.
+In `src/test/data/`, data meant for testing purposes are stored. While keeping the `address` field in the json files
+does not cause the tests to fail, it is not good practice to let cruft from old features accumulate.
**`invalidPersonAddressBook.json`:**
```json
{
- "persons": [ {
- "name": "Person with invalid name field: Ha!ns Mu@ster",
- "phone": "9482424",
- "email": "hans@example.com",
- "address": "4th street"
- } ]
+ "developers": [
+ {
+ "name": "Person with invalid name field: Ha!ns Mu@ster",
+ "phone": "9482424",
+ "email": "hans@example.com",
+ "address": "4th street"
+ }
+ ]
}
```
diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md
index 4fb62a83ef6..3485a78b8f1 100644
--- a/docs/tutorials/TracingCode.md
+++ b/docs/tutorials/TracingCode.md
@@ -3,18 +3,23 @@ layout: page
title: "Tutorial: Tracing code"
---
-> Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. …\[Therefore,\] making it easy to read makes it easier to write.
+> Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as
+> part of the effort to write new code. …\[Therefore,\] making it easy to read makes it easier to write.
>
-> — Robert C. Martin Clean Code: A Handbook of Agile Software Craftsmanship
+> — Robert C. Martin Clean Code: A Handbook of Agile Software Craftsmanship
-When trying to understand an unfamiliar code base, one common strategy used is to trace some representative execution path through the code base. One easy way to trace an execution path is to use a debugger to step through the code. In this tutorial, you will be using the IntelliJ IDEA’s debugger to trace the execution path of a specific user command.
+When trying to understand an unfamiliar code base, one common strategy used is to trace some representative execution
+path through the code base. One easy way to trace an execution path is to use a debugger to step through the code. In
+this tutorial, you will be using the IntelliJ IDEA’s debugger to trace the execution path of a specific user command.
* Table of Contents
-{:toc}
+ {:toc}
## Before we start
-Before we jump into the code, it is useful to get an idea of the overall structure and the high-level behavior of the application. This is provided in the 'Architecture' section of the developer guide. In particular, the architecture diagram (reproduced below), tells us that the App consists of several components.
+Before we jump into the code, it is useful to get an idea of the overall structure and the high-level behavior of the
+application. This is provided in the 'Architecture' section of the developer guide. In particular, the architecture
+diagram (reproduced below), tells us that the App consists of several components.
![ArchitectureDiagram](../images/ArchitectureDiagram.png)
@@ -22,33 +27,49 @@ It also has a sequence diagram (reproduced below) that tells us how a command pr
-Note how the diagram shows only the execution flows _between_ the main components. That is, it does not show details of the execution path *inside* each component. By hiding those details, the diagram aims to inform the reader about the overall execution path of a command without overwhelming the reader with too much details. In this tutorial, you aim to find those omitted details so that you get a more in-depth understanding of how the code works.
+Note how the diagram shows only the execution flows _between_ the main components. That is, it does not show details of
+the execution path *inside* each component. By hiding those details, the diagram aims to inform the reader about the
+overall execution path of a command without overwhelming the reader with too much details. In this tutorial, you aim to
+find those omitted details so that you get a more in-depth understanding of how the code works.
Before we proceed, ensure that you have done the following:
+
1. Read the [*Architecture* section of the DG](../DeveloperGuide.md#architecture)
1. Set up the project in Intellij IDEA
1. Learn basic debugging features of Intellij IDEA
- * If you are using a different IDE, we'll leave it to you to figure out the equivalent feature to use in your IDE.
- * If you are not using an IDE, we'll let you figure out how to achieve the same using your coding toolchain.
+ * If you are using a different IDE, we'll leave it to you to figure out the equivalent feature to use in your IDE.
+ * If you are not using an IDE, we'll let you figure out how to achieve the same using your coding toolchain.
## Setting a breakpoint
-As you know, the first step of debugging is to put in a breakpoint where you want the debugger to pause the execution. For example, if you are trying to understand how the App starts up, you would put a breakpoint in the first statement of the `main` method.
+As you know, the first step of debugging is to put in a breakpoint where you want the debugger to pause the execution.
+For example, if you are trying to understand how the App starts up, you would put a breakpoint in the first statement of
+the `main` method.
-In our case, we would want to begin the tracing at the very point where the App start processing user input (i.e., somewhere in the UI component), and then trace through how the execution proceeds through the UI component. However, the execution path through a GUI is often somewhat obscure due to various *event-driven mechanisms* used by GUI frameworks, which happens to be the case here too. Therefore, let us put the breakpoint where the `UI` transfers control to the `Logic` component.
+In our case, we would want to begin the tracing at the very point where the App start processing user input (i.e.,
+somewhere in the UI component), and then trace through how the execution proceeds through the UI component. However, the
+execution path through a GUI is often somewhat obscure due to various *event-driven mechanisms* used by GUI frameworks,
+which happens to be the case here too. Therefore, let us put the breakpoint where the `UI` transfers control to
+the `Logic` component.
-According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`.
+According to the sequence diagram you saw earlier (and repeated above for reference), the `UI` component yields control
+to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method
+that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`.
-:bulb: **Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
+:bulb: **Intellij Tip:** The ['**Search Everywhere
+**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find
+Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we
+are looking for a _method_ named `execute`, not simply the text `execute`.
-A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for.
+A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re
+looking for.
```java
public interface Logic {
@@ -65,31 +86,39 @@ public interface Logic {
```
But apparently, this is an interface, not a concrete implementation.
-That should be fine because the [Architecture section of the Developer Guide](../DeveloperGuide.html#architecture) tells us that components interact through interfaces. Here's the relevant diagram:
+That should be fine because the [Architecture section of the Developer Guide](../DeveloperGuide.html#architecture) tells
+us that components interact through interfaces. Here's the relevant diagram:
-Next, let's find out which statement(s) in the `UI` code is calling this method, thus transferring control from the `UI` to the `Logic`.
+Next, let's find out which statement(s) in the `UI` code is calling this method, thus transferring control from the `UI`
+to the `Logic`.
-:bulb: **Intellij Tip:** The ['**Find Usages**' feature](https://www.jetbrains.com/help/idea/find-highlight-usages.html#find-usages) can find from which parts of the code a class/method/variable is being used.
+:bulb: **Intellij Tip:** The ['**Find Usages
+**' feature](https://www.jetbrains.com/help/idea/find-highlight-usages.html#find-usages) can find from which parts of
+the code a class/method/variable is being used.
![`Find Usages` tool window. `Edit` \> `Find` \> `Find Usages`.](../images/tracing/FindUsages.png)
Bingo\! `MainWindow#executeCommand()` seems to be exactly what we’re looking for\!
-Now let’s set the breakpoint. First, double-click the item to reach the corresponding code. Once there, click on the left gutter to set a breakpoint, as shown below.
- ![LeftGutter](../images/tracing/LeftGutter.png)
+Now let’s set the breakpoint. First, double-click the item to reach the corresponding code. Once there, click on the
+left gutter to set a breakpoint, as shown below.
+![LeftGutter](../images/tracing/LeftGutter.png)
## Tracing the execution path
-Recall from the User Guide that the `edit` command has the format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…` For this tutorial we will be issuing the command `edit 1 n/Alice Yeoh`.
+Recall from the User Guide that the `edit` command has the
+format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…` For this tutorial we will be issuing the
+command `edit 1 n/Alice Yeoh`.
-:bulb: **Tip:** Over the course of the debugging session, you will encounter every major component in the application. Try to keep track of what happens inside the component and where the execution transfers to another component.
+:bulb: **Tip:** Over the course of the debugging session, you will encounter every major component in the application.
+Try to keep track of what happens inside the component and where the execution transfers to another component.
1. To start the debugging session, simply `Run` \> `Debug Main`
@@ -101,12 +130,16 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Use the _Show execution point_ feature to jump to the line of code that we stopped at:
![ShowExecutionPoint](../images/tracing/ShowExecutionPoint.png)
- `CommandResult commandResult = logic.execute(commandText);` is the line that you end up at (i.e., the place where we put the breakpoint).
+ `CommandResult commandResult = logic.execute(commandText);` is the line that you end up at (i.e., the place where we
+ put the breakpoint).
-1. We are interested in the `logic.execute(commandText)` portion of that line so let’s _Step in_ into that method call:
- ![StepInto](../images/tracing/StepInto.png)
+1. We are interested in the `logic.execute(commandText)` portion of that line so let’s _Step in_ into that method
+ call:
+ ![StepInto](../images/tracing/StepInto.png)
-1. We end up in `LogicManager#execute()` (not `Logic#execute` -- but this is expected because we know the `execute()` method in the `Logic` interface is actually implemented by the `LogicManager` class). Let’s take a look at the body of the method. Given below is the same code, with additional explanatory comments.
+1. We end up in `LogicManager#execute()` (not `Logic#execute` -- but this is expected because we know the `execute()`
+ method in the `Logic` interface is actually implemented by the `LogicManager` class). Let’s take a look at the body
+ of the method. Given below is the same code, with additional explanatory comments.
**LogicManager\#execute().**
@@ -136,12 +169,14 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
}
```
-1. `LogicManager#execute()` appears to delegate most of the heavy lifting to other components. Let’s take a closer look at each one.
+1. `LogicManager#execute()` appears to delegate most of the heavy lifting to other components. Let’s take a closer look
+ at each one.
1. _Step over_ the logging code since it is of no interest to us now.
![StepOver](../images/tracing/StepOver.png)
-1. _Step into_ the line where user input in parsed from a String to a Command, which should bring you to the `AddressBookParser#parseCommand()` method (partial code given below):
+1. _Step into_ the line where user input in parsed from a String to a Command, which should bring you to
+ the `AddressBookParser#parseCommand()` method (partial code given below):
``` java
public Command parseCommand(String userInput) throws ParseException {
...
@@ -150,12 +185,14 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
...
```
-1. _Step over_ the statements in that method until you reach the `switch` statement. The 'Variables' window now shows the value of both `commandWord` and `arguments`:
- ![Variables](../images/tracing/Variables.png)
+1. _Step over_ the statements in that method until you reach the `switch` statement. The 'Variables' window now shows
+ the value of both `commandWord` and `arguments`:
+ ![Variables](../images/tracing/Variables.png)
1. We see that the value of `commandWord` is now `edit` but `arguments` is still not processed in any meaningful way.
-1. Stepping through the `switch` block, we end up at a call to `EditCommandParser().parse()` as expected (because the command we typed is an edit command).
+1. Stepping through the `switch` block, we end up at a call to `EditCommandParser().parse()` as expected (because the
+ command we typed is an edit command).
``` java
...
@@ -164,93 +201,121 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
...
```
-1. Let’s see what `EditCommandParser#parse()` does by stepping into it. You might have to click the 'step into' button multiple times here because there are two method calls in that statement: `EditCommandParser()` and `parse()`.
+1. Let’s see what `EditCommandParser#parse()` does by stepping into it. You might have to click the 'step into' button
+ multiple times here because there are two method calls in that statement: `EditCommandParser()` and `parse()`.
:bulb: **Intellij Tip:** Sometimes, you might end up stepping into functions that are not of interest. Simply use the `step out` button to get out of them!
-1. Stepping through the method shows that it calls `ArgumentTokenizer#tokenize()` and `ParserUtil#parseIndex()` to obtain the arguments and index required.
+1. Stepping through the method shows that it calls `ArgumentTokenizer#tokenize()` and `ParserUtil#parseIndex()` to
+ obtain the arguments and index required.
-1. The rest of the method seems to exhaustively check for the existence of each possible parameter of the `edit` command and store any possible changes in an `EditPersonDescriptor`. Recall that we can verify the contents of `editPersonDesciptor` through the 'Variables' window.
+1. The rest of the method seems to exhaustively check for the existence of each possible parameter of the `edit` command
+ and store any possible changes in an `EditPersonDescriptor`. Recall that we can verify the contents
+ of `editPersonDesciptor` through the 'Variables' window.
![EditCommand](../images/tracing/EditCommand.png)
-1. As you just traced through some code involved in parsing a command, you can take a look at this class diagram to see where the various parsing-related classes you encountered fit into the design of the `Logic` component.
+1. As you just traced through some code involved in parsing a command, you can take a look at this class diagram to see
+ where the various parsing-related classes you encountered fit into the design of the `Logic` component.
1. Let’s continue stepping through until we return to `LogicManager#execute()`.
- The sequence diagram below shows the details of the execution path through the Logic component. Does the execution path you traced in the code so far match the diagram?
- ![Tracing an `edit` command through the Logic component](../images/tracing/LogicSequenceDiagram.png)
+ The sequence diagram below shows the details of the execution path through the Logic component. Does the execution
+ path you traced in the code so far match the diagram?
+ ![Tracing an `edit` command through the Logic component](../images/tracing/LogicSequenceDiagram.png)
-1. Now, step over until you read the statement that calls the `execute()` method of the `EditCommand` object received, and step into that `execute()` method (partial code given below):
+1. Now, step over until you read the statement that calls the `execute()` method of the `EditCommand` object received,
+ and step into that `execute()` method (partial code given below):
**`EditCommand#execute()`:**
``` java
@Override
public CommandResult execute(Model model) throws CommandException {
...
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
+ Person developerToEdit = lastShownList.get(index.getZeroBased());
+ Person editedDeveloper = createEditedPerson(developerToEdit, editPersonDescriptor);
+ if (!developerToEdit.isSamePerson(editedDeveloper) && model.hasPerson(editedDeveloper)) {
throw new CommandException(MESSAGE_DUPLICATE_PERSON);
}
- model.setPerson(personToEdit, editedPerson);
+ model.setPerson(developerToEdit, editedDeveloper);
model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson));
+ return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedDeveloper));
}
```
1. As suspected, `command#execute()` does indeed make changes to the `model` object. Specifically,
- * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the usual pattern) to update the person data.
- * it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ persons.
- FYI, The 'filtered list' is the list of persons resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the persons so that the user can see the edited person along with all other persons. If this was a `find` command, we would be setting that list to contain the search results instead.
- To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of persons is being tracked.
-
- * :bulb: This may be a good time to read through the [`Model` component section of the DG](../DeveloperGuide.html#model-component)
-
-1. As you step through the rest of the statements in the `EditCommand#execute()` method, you'll see that it creates a `CommandResult` object (containing information about the result of the execution) and returns it.
- Advancing the debugger by one more step should take you back to the middle of the `LogicManager#execute()` method.
-
-1. Given that you have already seen quite a few classes in the `Logic` component in action, see if you can identify in this partial class diagram some of the classes you've encountered so far, and see how they fit into the class structure of the `Logic` component:
-
- * :bulb: This may be a good time to read through the [`Logic` component section of the DG](../DeveloperGuide.html#logic-component)
-
-1. Similar to before, you can step over/into statements in the `LogicManager#execute()` method to examine how the control is transferred to the `Storage` component and what happens inside that component.
+ * it uses the `setPerson()` method (defined in the interface `Model` and implemented in `ModelManager` as per the
+ usual pattern) to update the developer data.
+ * it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_
+ developers.
+ FYI, The 'filtered list' is the list of developers resulting from the most recent operation that will be shown to
+ the user immediately after. For the `edit` command, we populate it with all the developers so that the user can
+ see the edited developer along with all other developers. If this was a `find` command, we would be setting that
+ list to contain the search results instead.
+ To provide some context, given below is the class diagram of the `Model` component. See if you can figure out
+ where the 'filtered list' of developers is being tracked.
+
+ * :bulb: This may be a good time to read through
+ the [`Model` component section of the DG](../DeveloperGuide.html#model-component)
+
+1. As you step through the rest of the statements in the `EditCommand#execute()` method, you'll see that it creates
+ a `CommandResult` object (containing information about the result of the execution) and returns it.
+ Advancing the debugger by one more step should take you back to the middle of the `LogicManager#execute()`
+ method.
+
+1. Given that you have already seen quite a few classes in the `Logic` component in action, see if you can identify in
+ this partial class diagram some of the classes you've encountered so far, and see how they fit into the class
+ structure of the `Logic` component:
+
+ * :bulb: This may be a good time to read through
+ the [`Logic` component section of the DG](../DeveloperGuide.html#logic-component)
+
+1. Similar to before, you can step over/into statements in the `LogicManager#execute()` method to examine how the
+ control is transferred to the `Storage` component and what happens inside that component.
:bulb: **Intellij Tip:** When trying to step into a statement such as `storage.saveAddressBook(model.getAddressBook())` which contains multiple method calls, Intellij will let you choose (by clicking) which one you want to step into.
-1. As you step through the code inside the `Storage` component, you will eventually arrive at the `JsonAddressBook#saveAddressBook()` method which calls the `JsonSerializableAddressBook` constructor, to create an object that can be _serialized_ (i.e., stored in storage medium) in JSON format. That constructor is given below (with added line breaks for easier readability):
+1. As you step through the code inside the `Storage` component, you will eventually arrive at
+ the `JsonAddressBook#saveAddressBook()` method which calls the `JsonSerializableAddressBook` constructor, to create
+ an object that can be _serialized_ (i.e., stored in storage medium) in JSON format. That constructor is given below (
+ with added line breaks for easier readability):
- **`JsonSerializableAddressBook` constructor:**
- ``` java
- /**
- * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use.
- *
- * @param source future changes to this will not affect the created
- * {@code JsonSerializableAddressBook}.
- */
- public JsonSerializableAddressBook(ReadOnlyAddressBook source) {
- persons.addAll(
- source.getPersonList()
- .stream()
- .map(JsonAdaptedPerson::new)
- .collect(Collectors.toList()));
- }
- ```
+ **`JsonSerializableAddressBook` constructor:**
+ ``` java
+ /**
+ * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use.
+ *
+ * @param source future changes to this will not affect the created
+ * {@code JsonSerializableAddressBook}.
+ */
+ public JsonSerializableAddressBook(ReadOnlyAddressBook source) {
+ developers.addAll(
+ source.getPersonList()
+ .stream()
+ .map(JsonAdaptedPerson::new)
+ .collect(Collectors.toList()));
+ }
+ ```
-1. It appears that a `JsonAdaptedPerson` is created for each `Person` and then added to the `JsonSerializableAddressBook`.
- This is because regular Java objects need to go through an _adaptation_ for them to be suitable to be saved in JSON format.
+1. It appears that a `JsonAdaptedPerson` is created for each `Person` and then added to
+ the `JsonSerializableAddressBook`.
+ This is because regular Java objects need to go through an _adaptation_ for them to be suitable to be saved in JSON
+ format.
-1. While you are stepping through the classes in the `Storage` component, here is the component's class diagram to help you understand how those classes fit into the structure of the component.
+1. While you are stepping through the classes in the `Storage` component, here is the component's class diagram to help
+ you understand how those classes fit into the structure of the component.
- * :bulb: This may be a good time to read through the [`Storage` component section of the DG](../DeveloperGuide.html#storage-component)
+ * :bulb: This may be a good time to read through
+ the [`Storage` component section of the DG](../DeveloperGuide.html#storage-component)
-1. We can continue to step through until you reach the end of the `LogicManager#execute()` method and return to the `MainWindow#executeCommand()` method (the place where we put the original breakpoint).
+1. We can continue to step through until you reach the end of the `LogicManager#execute()` method and return to
+ the `MainWindow#executeCommand()` method (the place where we put the original breakpoint).
1. Stepping into `resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());`, we end up in:
- **`ResultDisplay#setFeedbackToUser()`**
+ **`ResultDisplay#setFeedbackToUser()`**
``` java
public void setFeedbackToUser(String feedbackToUser) {
requireNonNull(feedbackToUser);
@@ -259,43 +324,45 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
```
1. Finally, you can step through until you reach the end of`MainWindow#executeCommand()`.
- :bulb: This may be a good time to read through the [`UI` component section of the DG](../DeveloperGuide.html#ui-component)
-
+ :bulb: This may be a good time to read through
+ the [`UI` component section of the DG](../DeveloperGuide.html#ui-component)
## Conclusion
-In this tutorial, we traced a valid edit command from raw user input to the result being displayed to the user. From this tutorial, you learned more about how the various components work together to produce a response to a user command.
+In this tutorial, we traced a valid edit command from raw user input to the result being displayed to the user. From
+this tutorial, you learned more about how the various components work together to produce a response to a user command.
-Here are some quick questions you can try to answer based on your execution path tracing. In some cases, you can do further tracing for the given commands to find exactly what happens.
+Here are some quick questions you can try to answer based on your execution path tracing. In some cases, you can do
+further tracing for the given commands to find exactly what happens.
-1. In this tutorial, we traced the "happy path" (i.e., no errors). What
- do you think will happen if we traced the following commands
- instead? What exceptions do you think will be thrown (if any), where
- will the exceptions be thrown and where will they be handled?
+1. In this tutorial, we traced the "happy path" (i.e., no errors). What
+ do you think will happen if we traced the following commands
+ instead? What exceptions do you think will be thrown (if any), where
+ will the exceptions be thrown and where will they be handled?
- 1. `redit 1 n/Alice Yu`
+ 1. `redit 1 n/Alice Yu`
- 2. `edit 0 n/Alice Yu`
+ 2. `edit 0 n/Alice Yu`
- 3. `edit 1 n/Alex Yeoh`
+ 3. `edit 1 n/Alex Yeoh`
- 4. `edit 1`
+ 4. `edit 1`
- 5. `edit 1 n/アリス ユー`
+ 5. `edit 1 n/アリス ユー`
- 6. `edit 1 t/one t/two t/three t/one`
+ 6. `edit 1 t/one t/two t/three t/one`
-2. What components will you have to modify to perform the following
- enhancements to the application?
+2. What components will you have to modify to perform the following
+ enhancements to the application?
- 1. Make command words case-insensitive
+ 1. Make command words case-insensitive
- 2. Allow `delete` to remove more than one index at a time
+ 2. Allow `delete` to remove more than one index at a time
- 3. Save the address book in the CSV format instead
+ 3. Save the address book in the CSV format instead
- 4. Add a new command
+ 4. Add a new command
- 5. Add a new field to `Person`
+ 5. Add a new field to `Person`
- 6. Add a new entity to the address book
+ 6. Add a new entity to the address book
diff --git a/gradle.properties b/gradle.properties
index 40764dc1791..c75a9af3404 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,5 @@
org.gradle.parallel=false
org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Xmx1024m -Dfile.encoding=utf-8
-
# TODO: This is a workaround for a JDK11 bug which causes test coverage upload to fail.
# Remove it when https://bugs.openjdk.java.net/browse/JDK-8221253 is fixed.
systemProp.jdk.tls.client.protocols="TLSv1,TLSv1.1,TLSv1.2"
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index e708b1c023e..41d9927a4d4 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/seedu/address/AppParameters.java
index 3d603622d4e..0fc21bcdfb4 100644
--- a/src/main/java/seedu/address/AppParameters.java
+++ b/src/main/java/seedu/address/AppParameters.java
@@ -19,14 +19,6 @@ public class AppParameters {
private Path configPath;
- public Path getConfigPath() {
- return configPath;
- }
-
- public void setConfigPath(Path configPath) {
- this.configPath = configPath;
- }
-
/**
* Parses the application command-line parameters.
*/
@@ -44,6 +36,14 @@ public static AppParameters parse(Application.Parameters parameters) {
return appParameters;
}
+ public Path getConfigPath() {
+ return configPath;
+ }
+
+ public void setConfigPath(Path configPath) {
+ this.configPath = configPath;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/address/Main.java
index ec1b7958746..f14473af111 100644
--- a/src/main/java/seedu/address/Main.java
+++ b/src/main/java/seedu/address/Main.java
@@ -7,17 +7,17 @@
/**
* The main entry point to the application.
- *
+ *
* This is a workaround for the following error when MainApp is made the
* entry point of the application:
- *
- * Error: JavaFX runtime components are missing, and are required to run this application
- *
+ *
+ * Error: JavaFX runtime components are missing, and are required to run this application
+ *
* The reason is that MainApp extends Application. In that case, the
* LauncherHelper will check for the javafx.graphics module to be present
* as a named module. We don't use JavaFX via the module system so it can't
* find the javafx.graphics module, and so the launch is aborted.
- *
+ *
* By having a separate main class (Main) that doesn't extend Application
* to be the entry point of the application, we avoid this issue.
*/
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 3d6bd06d5af..77a90798a34 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/address/MainApp.java
@@ -36,7 +36,7 @@
*/
public class MainApp extends Application {
- public static final Version VERSION = new Version(0, 2, 2, true);
+ public static final Version VERSION = new Version(1, 4, 0, true);
private static final Logger logger = LogsCenter.getLogger(MainApp.class);
diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/address/commons/core/LogsCenter.java
index 8cf8e15a0f0..b3cd9b0e3de 100644
--- a/src/main/java/seedu/address/commons/core/LogsCenter.java
+++ b/src/main/java/seedu/address/commons/core/LogsCenter.java
@@ -14,8 +14,8 @@
* Configures and manages loggers and handlers, including their logging level
* Named {@link Logger}s can be obtained from this class
* These loggers have been configured to output messages to the console and a {@code .log} file by default,
- * at the {@code INFO} level. A new {@code .log} file with a new numbering will be created after the log
- * file reaches 5MB big, up to a maximum of 5 files.
+ * at the {@code INFO} level. A new {@code .log} file with a new numbering will be created after the log
+ * file reaches 5MB big, up to a maximum of 5 files.
*/
public class LogsCenter {
private static final int MAX_FILE_COUNT = 5;
diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/address/commons/core/Version.java
index 491d24559b4..1e3c2882da9 100644
--- a/src/main/java/seedu/address/commons/core/Version.java
+++ b/src/main/java/seedu/address/commons/core/Version.java
@@ -32,24 +32,9 @@ public Version(int major, int minor, int patch, boolean isEarlyAccess) {
this.isEarlyAccess = isEarlyAccess;
}
- public int getMajor() {
- return major;
- }
-
- public int getMinor() {
- return minor;
- }
-
- public int getPatch() {
- return patch;
- }
-
- public boolean isEarlyAccess() {
- return isEarlyAccess;
- }
-
/**
* Parses a version number string in the format V1.2.3.
+ *
* @param versionString version number string
* @return a Version object
*/
@@ -67,6 +52,22 @@ public static Version fromString(String versionString) throws IllegalArgumentExc
versionMatcher.group(4) == null ? false : true);
}
+ public int getMajor() {
+ return major;
+ }
+
+ public int getMinor() {
+ return minor;
+ }
+
+ public int getPatch() {
+ return patch;
+ }
+
+ public boolean isEarlyAccess() {
+ return isEarlyAccess;
+ }
+
@JsonValue
public String toString() {
return String.format("V%d.%d.%d%s", major, minor, patch, isEarlyAccess ? "ea" : "");
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..6b0d846b4c9 100644
--- a/src/main/java/seedu/address/commons/core/index/Index.java
+++ b/src/main/java/seedu/address/commons/core/index/Index.java
@@ -4,7 +4,7 @@
/**
* Represents a zero-based or one-based index.
- *
+ *
* {@code Index} should be used right from the start (when parsing in a new user input), so that if the current
* component wants to communicate with another component, it can send an {@code Index} to avoid having to know what
* base the other component is using for its index. However, after receiving the {@code Index}, that component can
@@ -25,14 +25,6 @@ private Index(int zeroBasedIndex) {
this.zeroBasedIndex = zeroBasedIndex;
}
- public int getZeroBased() {
- return zeroBasedIndex;
- }
-
- public int getOneBased() {
- return zeroBasedIndex + 1;
- }
-
/**
* Creates a new {@code Index} using a zero-based index.
*/
@@ -47,6 +39,14 @@ public static Index fromOneBased(int oneBasedIndex) {
return new Index(oneBasedIndex - 1);
}
+ public int getZeroBased() {
+ return zeroBasedIndex;
+ }
+
+ public int getOneBased() {
+ return zeroBasedIndex + 1;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
index 19124db485c..651ce290208 100644
--- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
+++ b/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java
@@ -13,7 +13,7 @@ public IllegalValueException(String message) {
/**
* @param message should contain relevant information on the failed constraint(s)
- * @param cause of the main exception
+ * @param cause of the main exception
*/
public IllegalValueException(String message, Throwable cause) {
super(message, cause);
diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/address/commons/util/CollectionUtil.java
index eafe4dfd681..942edda97d7 100644
--- a/src/main/java/seedu/address/commons/util/CollectionUtil.java
+++ b/src/main/java/seedu/address/commons/util/CollectionUtil.java
@@ -12,7 +12,9 @@
*/
public class CollectionUtil {
- /** @see #requireAllNonNull(Collection) */
+ /**
+ * @see #requireAllNonNull(Collection)
+ */
public static void requireAllNonNull(Object... items) {
requireNonNull(items);
Stream.of(items).forEach(Objects::requireNonNull);
diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/address/commons/util/FileUtil.java
index b1e2767cdd9..f811215474c 100644
--- a/src/main/java/seedu/address/commons/util/FileUtil.java
+++ b/src/main/java/seedu/address/commons/util/FileUtil.java
@@ -20,6 +20,7 @@ public static boolean isFileExists(Path file) {
/**
* Returns true if {@code path} can be converted into a {@code Path} via {@link Paths#get(String)},
* otherwise returns false.
+ *
* @param path A string representing the file path. Cannot be null.
*/
public static boolean isValidPath(String path) {
@@ -33,6 +34,7 @@ public static boolean isValidPath(String path) {
/**
* Creates a file if it does not exist along with its missing parent directories.
+ *
* @throws IOException if the file or directory cannot be created.
*/
public static void createIfMissing(Path file) throws IOException {
diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/address/commons/util/JsonUtil.java
index 100cb16c395..cc2d698adb0 100644
--- a/src/main/java/seedu/address/commons/util/JsonUtil.java
+++ b/src/main/java/seedu/address/commons/util/JsonUtil.java
@@ -52,7 +52,7 @@ static T deserializeObjectFromJsonFile(Path jsonFile, Class classOfObject
* Returns the JSON object from the given file or {@code Optional.empty()} object if the file is not found.
* If any values are missing from the file, default values will be used, as long as the file is a valid JSON file.
*
- * @param filePath cannot be null.
+ * @param filePath cannot be null.
* @param classOfObjectToDeserialize JSON file has to correspond to the structure in the class given here.
* @throws DataLoadingException if loading of the JSON file failed.
*/
@@ -80,6 +80,7 @@ public static Optional readJsonFile(
/**
* Saves the Json object to the specified file.
* Overwrites existing file if it exists, creates a new file if it doesn't.
+ *
* @param jsonFile cannot be null
* @param filePath cannot be null
* @throws IOException if there was an error during writing to the file
@@ -94,6 +95,7 @@ public static void saveJsonFile(T jsonFile, Path filePath) throws IOExceptio
/**
* Converts a given string representation of a JSON data to instance of a class
+ *
* @param The generic type to create an instance of
* @return The instance of T with the specified values in the JSON string
*/
@@ -103,8 +105,9 @@ public static T fromJsonString(String json, Class instanceClass) throws I
/**
* Converts a given instance of a class into its JSON data string representation
+ *
* @param instance The T object to be converted into the JSON string
- * @param The generic type to create an instance of
+ * @param The generic type to create an instance of
* @return JSON data representation of the given class instance, in string
*/
public static String toJsonString(T instance) throws JsonProcessingException {
@@ -129,7 +132,6 @@ protected Level _deserialize(String value, DeserializationContext ctxt) {
* Gets the logging level that matches loggingLevelString
*
* Returns null if there are no matches
- *
*/
private Level getLoggingLevel(String loggingLevelString) {
return Level.parse(loggingLevelString);
diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java
index 61cc8c9a1cb..57ce6d91bfb 100644
--- a/src/main/java/seedu/address/commons/util/StringUtil.java
+++ b/src/main/java/seedu/address/commons/util/StringUtil.java
@@ -14,14 +14,15 @@ public class StringUtil {
/**
* Returns true if the {@code sentence} contains the {@code word}.
- * Ignores case, but a full word match is required.
- *
examples:
+ * 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
+ * @param word cannot be null, cannot be empty, must be a single word
*/
public static boolean containsWordIgnoreCase(String sentence, String word) {
requireNonNull(sentence);
@@ -38,6 +39,32 @@ public static boolean containsWordIgnoreCase(String sentence, String word) {
.anyMatch(preppedWord::equalsIgnoreCase);
}
+ /**
+ * Returns true if the {@code sentence} contains the {@code word} as a partial word match.
+ * Ignores case, and it matches the word even if it's only a part of a larger word.
+ *
examples:
+ * containsPartialWordIgnoreCase("ABc def", "abc") == true
+ * containsPartialWordIgnoreCase("ABc def", "DEF") == true
+ * containsPartialWordIgnoreCase("ABc def", "AB") == true
+ *
+ *
+ * @param sentence cannot be null
+ * @param word cannot be null, cannot be empty
+ */
+ public static boolean containsPartialWordIgnoreCase(String sentence, String word) {
+ requireNonNull(sentence);
+ requireNonNull(word);
+
+ String preppedWord = word.trim();
+ checkArgument(!preppedWord.isEmpty(), "Word parameter cannot be empty");
+
+ String preppedSentence = sentence;
+ String[] wordsInPreppedSentence = preppedSentence.split("\\s+");
+
+ return Arrays.stream(wordsInPreppedSentence)
+ .anyMatch(wordInSentence -> wordInSentence.toLowerCase().contains(preppedWord.toLowerCase()));
+ }
+
/**
* Returns a detailed message of the t, including the stack trace.
*/
@@ -53,6 +80,7 @@ public static String getDetails(Throwable t) {
* 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) {
diff --git a/src/main/java/seedu/address/commons/util/ToStringBuilder.java b/src/main/java/seedu/address/commons/util/ToStringBuilder.java
index d979b926734..9abdc755f39 100644
--- a/src/main/java/seedu/address/commons/util/ToStringBuilder.java
+++ b/src/main/java/seedu/address/commons/util/ToStringBuilder.java
@@ -30,7 +30,7 @@ public ToStringBuilder(Object object) {
/**
* Adds a field name/value pair to the output string.
*
- * @param fieldName The name of the field.
+ * @param fieldName The name of the field.
* @param fieldValue The value of the field.
* @return A reference to this {@code ToStringBuilder} object, allowing method calls to be chained.
*/
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..743149b250a 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -8,7 +8,8 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
+import seedu.address.model.client.Client;
+import seedu.address.model.developer.Developer;
/**
* API of the Logic component
@@ -16,10 +17,11 @@
public interface Logic {
/**
* Executes the command and returns the result.
+ *
* @param commandText The command as entered by the user.
* @return the result of the command execution.
* @throws CommandException If an error occurs during command execution.
- * @throws ParseException If an error occurs during parsing.
+ * @throws ParseException If an error occurs during parsing.
*/
CommandResult execute(String commandText) throws CommandException, ParseException;
@@ -30,8 +32,14 @@ public interface Logic {
*/
ReadOnlyAddressBook getAddressBook();
- /** Returns an unmodifiable view of the filtered list of persons */
- ObservableList getFilteredPersonList();
+ /**
+ * Returns an unmodifiable view of the filtered list of developers
+ */
+ ObservableList getFilteredDeveloperList();
+
+ ObservableList getFilteredClientList();
+
+ ObservableList getFilteredProjectList();
/**
* Returns the user prefs' address book file path.
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 5aa3b91c7d0..427777c6b0d 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -10,12 +10,14 @@
import seedu.address.commons.core.LogsCenter;
import seedu.address.logic.commands.Command;
import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.LockCommand;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.AddressBookParser;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Person;
+import seedu.address.model.client.Client;
+import seedu.address.model.developer.Developer;
import seedu.address.storage.Storage;
/**
@@ -40,6 +42,7 @@ public LogicManager(Model model, Storage storage) {
this.model = model;
this.storage = storage;
addressBookParser = new AddressBookParser();
+ new LockCommand().execute(model);
}
@Override
@@ -67,8 +70,18 @@ public ReadOnlyAddressBook getAddressBook() {
}
@Override
- public ObservableList getFilteredPersonList() {
- return model.getFilteredPersonList();
+ public ObservableList getFilteredDeveloperList() {
+ return model.getFilteredDeveloperList();
+ }
+
+ @Override
+ public ObservableList getFilteredClientList() {
+ return model.getFilteredClientList();
+ }
+
+ @Override
+ public ObservableList getFilteredProjectList() {
+ return model.getFilteredProjectList();
}
@Override
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
index ecd32c31b53..58e6479abbb 100644
--- a/src/main/java/seedu/address/logic/Messages.java
+++ b/src/main/java/seedu/address/logic/Messages.java
@@ -5,19 +5,73 @@
import java.util.stream.Stream;
import seedu.address.logic.parser.Prefix;
-import seedu.address.model.person.Person;
+import seedu.address.model.client.Client;
+import seedu.address.model.developer.Developer;
/**
* Container for user visible messages.
*/
public class Messages {
- public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
+ public static final String MESSAGE_UNKNOWN_COMMAND =
+ "Unknown command!\n"
+ + "Please provide your command in the following format: -.\n"
+ + "Example: list-developer, find-client n/John, delete-project 1";
+
+ public static final String MESSAGE_VALID_LOCKED_COMMANDS = "Valid commands are: \n unlock, help, exit";
+ public static final String MESSAGE_VALID_UNLOCKED_COMMANDS = "Type \"help\" to see the list of valid commands in"
+ + " User Guide!";
public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
- public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
- public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
+ public static final String MESSAGE_INVALID_FILE = "File does not exist!\n";
+ public static final String MESSAGE_INVALID_DEVELOPER_DISPLAYED_INDEX = "The developer index provided is invalid!";
+ public static final String MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX = "The client index provided is invalid!";
+ public static final String MESSAGE_INVALID_PROJECT_DISPLAYED_INDEX = "The project index provided is invalid!";
+ public static final String MESSAGE_INVALID_DEADLINE_DISPLAYED_INDEX = "The deadline index provided is invalid!";
+ public static final String MESSAGE_INAPPLICABLE_PREFIX_USED =
+ "You tried to edit an inapplicable field! Please check "
+ + "the prefixes used and try again. \n%1$s";
+ public static final String MESSAGE_NONEXISTENT_PROJECT = "There is no existing Project with the name: %1$s!";
public static final String MESSAGE_DUPLICATE_FIELDS =
- "Multiple values specified for the following single-valued field(s): ";
+ "Multiple values specified for the following single-valued field(s): ";
+
+ public static String getMessageDevelopersListedOverview(int count) {
+ if (count == 0) {
+ return "There are no developers with matching information.";
+ } else if (count == 1) {
+ return "This is the 1 developer with matching information.";
+ } else {
+ return String.format("These are the %d developers with matching information.", count);
+ }
+ }
+
+ public static String getMessageClientsListedOverview(int count) {
+ if (count == 0) {
+ return "There are no clients with matching information.";
+ } else if (count == 1) {
+ return "This is the 1 client with matching information.";
+ } else {
+ return String.format("These are the %d clients with matching information.", count);
+ }
+ }
+
+ public static String getMessageProjectsListedOverview(int count) {
+ if (count == 0) {
+ return "There are no projects with matching information.";
+ } else if (count == 1) {
+ return "This is the 1 project with matching information.";
+ } else {
+ return String.format("These are the %d projects with matching information.", count);
+ }
+ }
+ public static String getMessageDeadlinesListedOverview(int count) {
+ if (count == 0) {
+ return "There are no deadlines with matching information.";
+ } else if (count == 1) {
+ return "This is the 1 deadline with matching information.";
+ } else {
+ return String.format("These are the %d deadlines with matching information.", count);
+ }
+ }
/**
* Returns an error message indicating the duplicate prefixes.
@@ -32,19 +86,79 @@ public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePref
}
/**
- * Formats the {@code person} for display to the user.
+ * Formats the {@code developer} for display to the user.
+ */
+ public static String format(Developer developer) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(developer.getName())
+ .append("; \nPhone: ")
+ .append(developer.getPhone())
+ .append("; \nEmail: ")
+ .append(developer.getEmail())
+ .append("; \nAddress: ")
+ .append(developer.getAddress())
+ .append("; \nDate Joined: ")
+ .append(developer.getDateJoined())
+ .append("; \nRole: ")
+ .append(developer.getRole())
+ .append("; \nSalary: ")
+ .append(developer.getSalary())
+ .append("; \nProjects: ");
+ developer.getProjects().forEach(t -> builder.append(t).append(" "));
+ return builder.toString();
+ }
+
+ /**
+ * Formats a client's information into a string.
+ *
+ * @param client The client to format.
+ * @return A string representing the formatted client information.
+ */
+ public static String format(Client client) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(client.getName())
+ .append("; \nPhone: ")
+ .append(client.getPhone())
+ .append("; \nEmail: ")
+ .append(client.getEmail())
+ .append("; \nAddress: ")
+ .append(client.getAddress())
+ .append("; \nOrganisation: ")
+ .append(client.getOrganisation())
+ .append("; \nRole: ")
+ .append(client.getRole())
+ .append("; \nDocument: ")
+ .append(client.getDocument())
+ .append("; \nProjects: ");
+ client.getProjects().forEach(t -> builder.append(t).append(" "));
+ return builder.toString();
+ }
+
+ /**
+ * Formats a project's information into a string.
+ *
+ * @param project The project to format.
+ * @return A string representing the formatted project information.
+ */
+ public static Object format(seedu.address.model.project.Project project) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(project.getName())
+ .append(";\nDescription: ")
+ .append(project.getProjectDescription())
+ .append(";\nDeadlines:\n");
+ project.getProjectDeadlines().forEach(t -> builder.append(t.getPrintedStringRepresentation()).append("\n"));
+ return builder.toString();
+ }
+
+ /**
+ * Formats a role into a string.
+ *
+ * @param role The role to format.
+ * @return A string representing the formatted role.
*/
- public static String format(Person person) {
+ public static String format(String role) {
final StringBuilder builder = new StringBuilder();
- builder.append(person.getName())
- .append("; Phone: ")
- .append(person.getPhone())
- .append("; Email: ")
- .append(person.getEmail())
- .append("; Address: ")
- .append(person.getAddress())
- .append("; Tags: ");
- person.getTags().forEach(builder::append);
+ builder.append(role);
return builder.toString();
}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
deleted file mode 100644
index 5d7185a9680..00000000000
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Person;
-
-/**
- * Adds a person to the address book.
- */
-public class AddCommand extends Command {
-
- public static final String COMMAND_WORD = "add";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
- + "Parameters: "
- + PREFIX_NAME + "NAME "
- + PREFIX_PHONE + "PHONE "
- + PREFIX_EMAIL + "EMAIL "
- + PREFIX_ADDRESS + "ADDRESS "
- + "[" + PREFIX_TAG + "TAG]...\n"
- + "Example: " + COMMAND_WORD + " "
- + PREFIX_NAME + "John Doe "
- + PREFIX_PHONE + "98765432 "
- + PREFIX_EMAIL + "johnd@example.com "
- + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
- + PREFIX_TAG + "friends "
- + PREFIX_TAG + "owesMoney";
-
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
-
- private final Person toAdd;
-
- /**
- * Creates an AddCommand to add the specified {@code Person}
- */
- public AddCommand(Person person) {
- requireNonNull(person);
- toAdd = person;
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
-
- if (model.hasPerson(toAdd)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
- }
-
- model.addPerson(toAdd);
- return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd)));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof AddCommand)) {
- return false;
- }
-
- AddCommand otherAddCommand = (AddCommand) other;
- return toAdd.equals(otherAddCommand.toAdd);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("toAdd", toAdd)
- .toString();
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/ChangePasswordCommand.java b/src/main/java/seedu/address/logic/commands/ChangePasswordCommand.java
new file mode 100644
index 00000000000..0e7512a680d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ChangePasswordCommand.java
@@ -0,0 +1,43 @@
+/**
+ * Command to change the user's password.
+ */
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.model.Model;
+import seedu.address.model.Password;
+
+/**
+ * Changes the user's password to a new one.
+ */
+public class ChangePasswordCommand extends Command {
+ public static final String COMMAND_WORD = "change-password";
+
+ // Usage message for the command
+ public static final Object MESSAGE_USAGE = COMMAND_WORD + ": Please change the password to the correct format\n"
+ + Password.MESSAGE_CONSTRAINTS + "\n"
+ + "Example: " + COMMAND_WORD + " pw/Password123! npw/NewPass987!";
+
+ private String currentPw;
+ private String newPw;
+
+ /**
+ * Constructs a {@code ChangePasswordCommand} with the current password and the new password.
+ *
+ * @param currentPw The current password.
+ * @param newPw The new password to replace the current password.
+ */
+ public ChangePasswordCommand(String currentPw, String newPw) {
+ this.currentPw = currentPw;
+ this.newPw = newPw;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ assert model != null : "Model cannot be null";
+ requireNonNull(model);
+ String result = Password.changePassword(currentPw, newPw);
+ return new CommandResult(result, TabIndex.Developer);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
index 9c86b1fa6e4..04405f09c86 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java
@@ -18,6 +18,7 @@ public class ClearCommand extends Command {
public CommandResult execute(Model model) {
requireNonNull(model);
model.setAddressBook(new AddressBook());
- return new CommandResult(MESSAGE_SUCCESS);
+ model.commitAddressBook(model, MESSAGE_SUCCESS, TabIndex.Developer);
+ return new CommandResult(MESSAGE_SUCCESS, TabIndex.Developer);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java
index 249b6072d0d..d4bbbdb85cd 100644
--- a/src/main/java/seedu/address/logic/commands/CommandResult.java
+++ b/src/main/java/seedu/address/logic/commands/CommandResult.java
@@ -13,27 +13,34 @@ public class CommandResult {
private final String feedbackToUser;
- /** Help information should be shown to the user. */
+ /**
+ * Help information should be shown to the user.
+ */
private final boolean showHelp;
- /** The application should exit. */
+ /**
+ * The application should exit.
+ */
private final boolean exit;
+ private final TabIndex index;
+
/**
* Constructs a {@code CommandResult} with the specified fields.
*/
- public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) {
+ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, TabIndex index) {
this.feedbackToUser = requireNonNull(feedbackToUser);
this.showHelp = showHelp;
this.exit = exit;
+ this.index = index;
}
/**
* Constructs a {@code CommandResult} with the specified {@code feedbackToUser},
* and other fields set to their default value.
*/
- public CommandResult(String feedbackToUser) {
- this(feedbackToUser, false, false);
+ public CommandResult(String feedbackToUser, TabIndex index) {
+ this(feedbackToUser, false, false, index);
}
public String getFeedbackToUser() {
@@ -62,12 +69,13 @@ public boolean equals(Object other) {
CommandResult otherCommandResult = (CommandResult) other;
return feedbackToUser.equals(otherCommandResult.feedbackToUser)
&& showHelp == otherCommandResult.showHelp
- && exit == otherCommandResult.exit;
+ && exit == otherCommandResult.exit
+ && index == otherCommandResult.index;
}
@Override
public int hashCode() {
- return Objects.hash(feedbackToUser, showHelp, exit);
+ return Objects.hash(feedbackToUser, showHelp, exit, index);
}
@Override
@@ -76,7 +84,11 @@ public String toString() {
.add("feedbackToUser", feedbackToUser)
.add("showHelp", showHelp)
.add("exit", exit)
+ .add("index", index.toString())
.toString();
}
+ public int getIndex() {
+ return index.ordinal();
+ }
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
deleted file mode 100644
index 1135ac19b74..00000000000
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Person;
-
-/**
- * Deletes a person identified using it's displayed index from the address book.
- */
-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";
-
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
-
- private final Index targetIndex;
-
- public DeleteCommand(Index targetIndex) {
- this.targetIndex = targetIndex;
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
-
- if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
- model.deletePerson(personToDelete);
- return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof DeleteCommand)) {
- return false;
- }
-
- DeleteCommand otherDeleteCommand = (DeleteCommand) other;
- return targetIndex.equals(otherDeleteCommand.targetIndex);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("targetIndex", targetIndex)
- .toString();
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
deleted file mode 100644
index 4b581c7331e..00000000000
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ /dev/null
@@ -1,242 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.CollectionUtil;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Edits the details of an existing person in the address book.
- */
-public class EditCommand extends Command {
-
- public static final String COMMAND_WORD = "edit";
-
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified "
- + "by the index number used in the displayed person list. "
- + "Existing values will be overwritten by the input values.\n"
- + "Parameters: INDEX (must be a positive integer) "
- + "[" + PREFIX_NAME + "NAME] "
- + "[" + PREFIX_PHONE + "PHONE] "
- + "[" + PREFIX_EMAIL + "EMAIL] "
- + "[" + PREFIX_ADDRESS + "ADDRESS] "
- + "[" + PREFIX_TAG + "TAG]...\n"
- + "Example: " + COMMAND_WORD + " 1 "
- + PREFIX_PHONE + "91234567 "
- + PREFIX_EMAIL + "johndoe@example.com";
-
- public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s";
- public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book.";
-
- private final Index index;
- private final EditPersonDescriptor editPersonDescriptor;
-
- /**
- * @param index of the person in the filtered person list to edit
- * @param editPersonDescriptor details to edit the person with
- */
- public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
- requireNonNull(index);
- requireNonNull(editPersonDescriptor);
-
- this.index = index;
- this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor);
- }
-
- @Override
- public CommandResult execute(Model model) throws CommandException {
- requireNonNull(model);
- List lastShownList = model.getFilteredPersonList();
-
- if (index.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
- }
-
- Person personToEdit = lastShownList.get(index.getZeroBased());
- Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor);
-
- if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) {
- throw new CommandException(MESSAGE_DUPLICATE_PERSON);
- }
-
- model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
- }
-
- /**
- * Creates and returns a {@code Person} with the details of {@code personToEdit}
- * edited with {@code editPersonDescriptor}.
- */
- private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) {
- assert personToEdit != null;
-
- Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName());
- Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone());
- Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail());
- Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress());
- Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
-
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditCommand)) {
- return false;
- }
-
- EditCommand otherEditCommand = (EditCommand) other;
- return index.equals(otherEditCommand.index)
- && editPersonDescriptor.equals(otherEditCommand.editPersonDescriptor);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("index", index)
- .add("editPersonDescriptor", editPersonDescriptor)
- .toString();
- }
-
- /**
- * Stores the details to edit the person with. Each non-empty field value will replace the
- * corresponding field value of the person.
- */
- public static class EditPersonDescriptor {
- private Name name;
- private Phone phone;
- private Email email;
- private Address address;
- private Set tags;
-
- public EditPersonDescriptor() {}
-
- /**
- * Copy constructor.
- * A defensive copy of {@code tags} is used internally.
- */
- public EditPersonDescriptor(EditPersonDescriptor toCopy) {
- setName(toCopy.name);
- setPhone(toCopy.phone);
- setEmail(toCopy.email);
- setAddress(toCopy.address);
- setTags(toCopy.tags);
- }
-
- /**
- * Returns true if at least one field is edited.
- */
- public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
- }
-
- public void setName(Name name) {
- this.name = name;
- }
-
- public Optional getName() {
- return Optional.ofNullable(name);
- }
-
- public void setPhone(Phone phone) {
- this.phone = phone;
- }
-
- public Optional getPhone() {
- return Optional.ofNullable(phone);
- }
-
- public void setEmail(Email email) {
- this.email = email;
- }
-
- public Optional getEmail() {
- return Optional.ofNullable(email);
- }
-
- public void setAddress(Address address) {
- this.address = address;
- }
-
- public Optional getAddress() {
- return Optional.ofNullable(address);
- }
-
- /**
- * Sets {@code tags} to this object's {@code tags}.
- * A defensive copy of {@code tags} is used internally.
- */
- public void setTags(Set tags) {
- this.tags = (tags != null) ? new HashSet<>(tags) : null;
- }
-
- /**
- * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
- * if modification is attempted.
- * Returns {@code Optional#empty()} if {@code tags} is null.
- */
- public Optional> getTags() {
- return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty();
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditPersonDescriptor)) {
- return false;
- }
-
- EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other;
- return Objects.equals(name, otherEditPersonDescriptor.name)
- && Objects.equals(phone, otherEditPersonDescriptor.phone)
- && Objects.equals(email, otherEditPersonDescriptor.email)
- && Objects.equals(address, otherEditPersonDescriptor.address)
- && Objects.equals(tags, otherEditPersonDescriptor.tags);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
- }
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java
index 3dd85a8ba90..dfe2b088274 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -13,7 +13,7 @@ public class ExitCommand extends Command {
@Override
public CommandResult execute(Model model) {
- return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true);
+ return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, TabIndex.Developer);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
deleted file mode 100644
index 72b9eddd3a7..00000000000
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-
-/**
- * Finds and lists all persons in address book whose name contains any of the argument keywords.
- * 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";
-
- private final NameContainsKeywordsPredicate predicate;
-
- public FindCommand(NameContainsKeywordsPredicate predicate) {
- this.predicate = predicate;
- }
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.updateFilteredPersonList(predicate);
- return new CommandResult(
- String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof FindCommand)) {
- return false;
- }
-
- FindCommand otherFindCommand = (FindCommand) other;
- return predicate.equals(otherFindCommand.predicate);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("predicate", predicate)
- .toString();
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java
index bf824f91bd0..7c03d3a25b1 100644
--- a/src/main/java/seedu/address/logic/commands/HelpCommand.java
+++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java
@@ -16,6 +16,6 @@ public class HelpCommand extends Command {
@Override
public CommandResult execute(Model model) {
- return new CommandResult(SHOWING_HELP_MESSAGE, true, false);
+ return new CommandResult(SHOWING_HELP_MESSAGE, true, false, TabIndex.Developer);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
deleted file mode 100644
index 84be6ad2596..00000000000
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package seedu.address.logic.commands;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
-
-import seedu.address.model.Model;
-
-/**
- * Lists all persons in the address book to the user.
- */
-public class ListCommand extends Command {
-
- public static final String COMMAND_WORD = "list";
-
- public static final String MESSAGE_SUCCESS = "Listed all persons";
-
-
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
- return new CommandResult(MESSAGE_SUCCESS);
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/LockCommand.java b/src/main/java/seedu/address/logic/commands/LockCommand.java
new file mode 100644
index 00000000000..a69748f5c3e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/LockCommand.java
@@ -0,0 +1,30 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_NO_CLIENT;
+import static seedu.address.model.Model.PREDICATE_SHOW_NO_DEVELOPER;
+import static seedu.address.model.Model.PREDICATE_SHOW_NO_PROJECT;
+
+import seedu.address.logic.parser.AddressBookParser;
+import seedu.address.model.Model;
+
+/**
+ * Command to lock the application.
+ */
+public class LockCommand extends Command {
+ public static final String COMMAND_WORD = "lock";
+
+ public static final String MESSAGE_SUCCESS = "Locked all data";
+
+
+ @Override
+ public CommandResult execute(Model model) {
+ assert model != null : "Model cannot be null";
+ requireNonNull(model);
+ model.updateFilteredClientList(PREDICATE_SHOW_NO_CLIENT);
+ model.updateFilteredDeveloperList(PREDICATE_SHOW_NO_DEVELOPER);
+ model.updateFilteredProjectList(PREDICATE_SHOW_NO_PROJECT);
+ AddressBookParser.lock();
+ return new CommandResult(MESSAGE_SUCCESS, TabIndex.Developer);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/RedoCommand.java b/src/main/java/seedu/address/logic/commands/RedoCommand.java
new file mode 100644
index 00000000000..17efa4b90e6
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/RedoCommand.java
@@ -0,0 +1,39 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.client.ClientRoles;
+import seedu.address.model.developer.DeveloperRoles;
+
+/**
+ * Represents a command to redo the most recent undone command in the address book.
+ * It restores the state of the address book to the state before the last undo operation.
+ */
+public class RedoCommand extends Command {
+ public static final String COMMAND_WORD = "redo";
+ public static final String MESSAGE_SUCCESS = "Redo successful! The change below has been redone:";
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.redoAddressBook(model);
+ String previousCommand = model.getPreviousCommandForRedo();
+ TabIndex index = model.getPreviousTabIndexForRedo();
+ handleRoleRedo(previousCommand);
+ return new CommandResult(MESSAGE_SUCCESS + "\n" + previousCommand, index);
+ }
+
+ private void handleRoleRedo(String previousCommand) {
+ if (previousCommand.contains("New role for client added: ")) {
+ ClientRoles.addClientRole(new ClientRoles(previousCommand.substring(27)));
+ } else if (previousCommand.contains("New role for developer added: ")) {
+ DeveloperRoles.addDeveloperRole(new DeveloperRoles(previousCommand.substring(30)));
+ } else if (previousCommand.contains("Role for clients deleted: ")) {
+ ClientRoles.deleteClientRole(new ClientRoles(previousCommand.substring(26)));
+ } else if (previousCommand.contains("Role for developers deleted: ")) {
+ DeveloperRoles.deleteDeveloperRole(new DeveloperRoles(previousCommand.substring(29)));
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/TabIndex.java b/src/main/java/seedu/address/logic/commands/TabIndex.java
new file mode 100644
index 00000000000..444c6167e71
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/TabIndex.java
@@ -0,0 +1,22 @@
+package seedu.address.logic.commands;
+
+/**
+ * Enumerates the different tabs available in the application's user interface.
+ * These tabs correspond to different categories or views within the application.
+ */
+public enum TabIndex {
+ /**
+ * Represents the "Developer" tab, which is used to display and manage developers.
+ */
+ Developer,
+
+ /**
+ * Represents the "Client" tab, which is used to display and manage clients.
+ */
+ Client,
+
+ /**
+ * Represents the "Project" tab, which is used to display and manage projects.
+ */
+ Project
+}
diff --git a/src/main/java/seedu/address/logic/commands/UndoCommand.java b/src/main/java/seedu/address/logic/commands/UndoCommand.java
new file mode 100644
index 00000000000..faffb39a91a
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UndoCommand.java
@@ -0,0 +1,49 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.client.ClientRoles;
+import seedu.address.model.developer.DeveloperRoles;
+
+/**
+ * Represents a command to undo the most recent change in the application.
+ * This allows users to revert the effects of the previous command.
+ */
+public class UndoCommand extends Command {
+ public static final String COMMAND_WORD = "undo";
+ public static final String MESSAGE_SUCCESS = "Undo successful! The change below has been undone: ";
+
+ /**
+ * Executes the undo operation by restoring the previous state of the model and
+ * retrieving information about the undone command.
+ *
+ * @param model The model from which to undo the most recent change.
+ * @return A CommandResult indicating the success of the undo operation and the
+ * details of the undone command.
+ * @throws CommandException If an error occurs during the undo operation.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ model.undoAddressBook(model);
+ String previousCommand = model.getPreviousCommandForUndo();
+ TabIndex index = model.getPreviousTabIndex();
+ // check if it is any of the role commands
+ handleRoleUndo(previousCommand);
+ return new CommandResult(MESSAGE_SUCCESS + "\n" + previousCommand, index);
+ }
+
+ private void handleRoleUndo(String previousCommand) {
+ if (previousCommand.contains("New role for client added: ")) {
+ ClientRoles.deleteClientRole(new ClientRoles(previousCommand.substring(27)));
+ } else if (previousCommand.contains("New role for developer added: ")) {
+ DeveloperRoles.deleteDeveloperRole(new DeveloperRoles(previousCommand.substring(30)));
+ } else if (previousCommand.contains("Role for clients deleted: ")) {
+ ClientRoles.addClientRole(new ClientRoles(previousCommand.substring(26)));
+ } else if (previousCommand.contains("Role for developers deleted: ")) {
+ DeveloperRoles.addDeveloperRole(new DeveloperRoles(previousCommand.substring(29)));
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/UnlockCommand.java b/src/main/java/seedu/address/logic/commands/UnlockCommand.java
new file mode 100644
index 00000000000..393e398cdd3
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UnlockCommand.java
@@ -0,0 +1,54 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_CLIENTS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_DEVELOPERS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PROJECTS;
+
+import seedu.address.logic.parser.AddressBookParser;
+import seedu.address.model.Model;
+import seedu.address.model.Password;
+
+/**
+ * Represents a command to unlock access to all data within the application
+ * using a password. Unlocking provides access to restricted data.
+ */
+public class UnlockCommand extends Command {
+ public static final String COMMAND_WORD = "unlock";
+
+ public static final String MESSAGE_SUCCESS = "Unlocked all data";
+ public static final String MESSAGE_FAILURE = "Incorrect Password!\n";
+ public static final Object MESSAGE_USAGE = COMMAND_WORD + ": Please unlock using correct format\n"
+ + "Example: " + COMMAND_WORD + " pw/Password123!";
+ private String input;
+
+ /**
+ * Creates an UnlockCommand to unlock data with the given password.
+ *
+ * @param input The password input provided by the user.
+ */
+ public UnlockCommand(String input) {
+ this.input = input;
+ }
+
+ /**
+ * Executes the unlock operation, which grants access to restricted data
+ * when the provided password is correct.
+ *
+ * @param model The model containing the data to be unlocked.
+ * @return A CommandResult indicating the success or failure of the unlock operation.
+ */
+ @Override
+ public CommandResult execute(Model model) {
+ assert model != null : "Model cannot be null";
+ requireNonNull(model);
+ if (Password.verifyPassword(input)) {
+ model.updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS);
+ model.updateFilteredDeveloperList(PREDICATE_SHOW_ALL_DEVELOPERS);
+ model.updateFilteredProjectList(PREDICATE_SHOW_ALL_PROJECTS);
+ AddressBookParser.unlock();
+ return new CommandResult(MESSAGE_SUCCESS, TabIndex.Developer);
+ }
+ return new CommandResult(String.format(MESSAGE_FAILURE, Password.MESSAGE_CONSTRAINTS), TabIndex.Developer);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/add/AddClientCommand.java b/src/main/java/seedu/address/logic/commands/add/AddClientCommand.java
new file mode 100644
index 00000000000..9dd91bb0d94
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/add/AddClientCommand.java
@@ -0,0 +1,106 @@
+package seedu.address.logic.commands.add;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOCUMENT;
+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_ORGANISATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.client.Client;
+
+/**
+ * Adds a client to the address book.
+ */
+public class AddClientCommand extends Command {
+
+ public static final String COMMAND_WORD = "add-client";
+ //Name name, Phone phone, Email email, Address address, Role role, Set projects,
+ // Name organisation, Document document
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a client to the address book. "
+ + "\n Parameters: "
+ + PREFIX_NAME + "NAME "
+ + PREFIX_PHONE + "PHONE "
+ + PREFIX_EMAIL + "EMAIL "
+ + PREFIX_ADDRESS + "ADDRESS "
+ + PREFIX_ROLE + "ROLE "
+ + "[" + PREFIX_PROJECT + "PROJECT]...\n"
+ + PREFIX_ORGANISATION + "ORGANISATION "
+ + PREFIX_DOCUMENT + "DOCUMENT \n"
+ + "Example: \n" + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_PHONE + "98765432 "
+ + PREFIX_EMAIL + "johnd@example.com "
+ + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
+ + PREFIX_ROLE + "Developer "
+ + PREFIX_PROJECT + "AndroidApp "
+ + PREFIX_PROJECT + "CustomWebsite "
+ + PREFIX_ORGANISATION + "Google "
+ + PREFIX_DOCUMENT + "google.com ";
+
+ public static final String MESSAGE_SUCCESS = "New client added: %1$s";
+ public static final String MESSAGE_DUPLICATE_CLIENT = "This client already exists in the address book!";
+
+ private final Client toAdd;
+
+ /**
+ * Creates an AddDeveloperCommand to add the specified {@code Developer}
+ */
+ public AddClientCommand(Client client) {
+ requireNonNull(client);
+ toAdd = client;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (model.hasClient(toAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_CLIENT);
+ }
+ String res = model.areProjectsValid(toAdd);
+ if (res != null) {
+ throw new CommandException(String.format(Messages.MESSAGE_NONEXISTENT_PROJECT, res));
+ }
+
+ String successMessage = String.format(MESSAGE_SUCCESS, Messages.format(toAdd));
+ TabIndex index = TabIndex.Client;
+
+ model.addClient(toAdd);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddClientCommand)) {
+ return false;
+ }
+
+ AddClientCommand otherAddClientCommand = (AddClientCommand) other;
+ return toAdd.equals(otherAddClientCommand.toAdd);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/add/AddDeveloperCommand.java b/src/main/java/seedu/address/logic/commands/add/AddDeveloperCommand.java
new file mode 100644
index 00000000000..d728417fe30
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/add/AddDeveloperCommand.java
@@ -0,0 +1,110 @@
+package seedu.address.logic.commands.add;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GITHUBID;
+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_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.developer.Developer;
+
+/**
+ * Adds a developer to the address book.
+ */
+public class AddDeveloperCommand extends Command {
+
+ public static final String COMMAND_WORD = "add-developer";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a developer to the address book. "
+ + "\n Parameters: "
+ + PREFIX_NAME + "NAME "
+ + PREFIX_PHONE + "PHONE "
+ + PREFIX_EMAIL + "EMAIL "
+ + PREFIX_ADDRESS + "ADDRESS "
+ + PREFIX_ROLE + "ROLE "
+ + "[" + PREFIX_PROJECT + "PROJECT]...\n"
+ + PREFIX_SALARY + "SALARY "
+ + "[" + PREFIX_DATEJOINED + "DATE JOINED] "
+ + PREFIX_GITHUBID + "GITHUBID "
+ + PREFIX_RATING + "RATING \n"
+ + "Example: \n" + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_PHONE + "98765432 "
+ + PREFIX_EMAIL + "johnd@example.com "
+ + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
+ + PREFIX_ROLE + "Developer "
+ + PREFIX_PROJECT + "AndroidApp "
+ + PREFIX_PROJECT + "CustomWebsite "
+ + PREFIX_SALARY + "4500 "
+ + PREFIX_DATEJOINED + "11-11-2023 "
+ + PREFIX_GITHUBID + "johng "
+ + PREFIX_RATING + "3";
+
+ public static final String MESSAGE_SUCCESS = "New developer added: %1$s";
+ public static final String MESSAGE_DUPLICATE_DEVELOPER = "This developer already exists in the address book!";
+
+ private final Developer toAdd;
+
+ /**
+ * Creates an AddDeveloperCommand to add the specified {@code Developer}
+ */
+ public AddDeveloperCommand(Developer developer) {
+ requireNonNull(developer);
+ toAdd = developer;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (model.hasDeveloper(toAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_DEVELOPER);
+ }
+ String res = model.areProjectsValid(toAdd);
+ if (res != null) {
+ throw new CommandException(String.format(Messages.MESSAGE_NONEXISTENT_PROJECT, res));
+ }
+
+ String successMessage = String.format(MESSAGE_SUCCESS, Messages.format(toAdd));
+ TabIndex index = TabIndex.Developer;
+
+ model.addDeveloper(toAdd);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddDeveloperCommand)) {
+ return false;
+ }
+
+ AddDeveloperCommand otherAddDeveloperCommand = (AddDeveloperCommand) other;
+ return toAdd.equals(otherAddDeveloperCommand.toAdd);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/add/AddProjectCommand.java b/src/main/java/seedu/address/logic/commands/add/AddProjectCommand.java
new file mode 100644
index 00000000000..fc95413d369
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/add/AddProjectCommand.java
@@ -0,0 +1,86 @@
+package seedu.address.logic.commands.add;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.project.Deadline;
+import seedu.address.model.project.Project;
+
+/**
+ * Adds a project to the address book.
+ */
+public class AddProjectCommand extends Command {
+
+ public static final String COMMAND_WORD = "add-project";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a project to the address book.\n"
+ + Deadline.MESSAGE_CONSTRAINTS + "\n"
+ + "Parameters: "
+ + PREFIX_NAME + "NAME "
+ + PREFIX_DESCRIPTION + "DESCRIPTION "
+ + "[" + PREFIX_DEADLINE + "DEADLINE_DATE,DEADLINE_DESCRIPTION,PRIORITY,IS_DONE]...\n"
+ + "Example: \n" + COMMAND_WORD + " "
+ + PREFIX_NAME + "JuiceApp "
+ + PREFIX_DESCRIPTION + "App to allow for different juices to be ordered "
+ + PREFIX_DEADLINE + "19-12-2023,Design backend,HIGH,0 "
+ + PREFIX_DEADLINE + "25-12-2023,Design frontend,MEDIUM,0 ";
+
+ public static final String MESSAGE_SUCCESS = "New project added: %1$s";
+ public static final String MESSAGE_DUPLICATE_PROJECT = "This project already exists in the address book!";
+
+ private final Project toAdd;
+
+ /**
+ * Creates an AddProjectCommand to add the specified {@code Developer}
+ */
+ public AddProjectCommand(Project project) {
+ requireNonNull(project);
+ toAdd = project;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (model.hasProject(toAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_PROJECT);
+ }
+
+ String successMessage = String.format(MESSAGE_SUCCESS, Messages.format(toAdd));
+ TabIndex index = TabIndex.Project;
+
+ model.addProject(toAdd);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof AddProjectCommand)) {
+ return false;
+ }
+
+ AddProjectCommand otherAddProjectCommand = (AddProjectCommand) other;
+ return toAdd.equals(otherAddProjectCommand.toAdd);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/addroles/AddClientRoleCommand.java b/src/main/java/seedu/address/logic/commands/addroles/AddClientRoleCommand.java
new file mode 100644
index 00000000000..8ed119d0467
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/addroles/AddClientRoleCommand.java
@@ -0,0 +1,96 @@
+package seedu.address.logic.commands.addroles;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.add.AddDeveloperCommand;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.client.ClientRoles;
+/**
+ * Represents a command to add a new role for clients to the address book.
+ * This allows users to define custom roles for clients.
+ */
+public class AddClientRoleCommand extends Command {
+ public static final String COMMAND_WORD = "add-client-role";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a role for clients to the address book. "
+ + "Parameters: " + PREFIX_ROLE + "ROLE "
+ + "Example: " + PREFIX_ROLE + "Developer ";
+
+ public static final String MESSAGE_SUCCESS = "New role for client added: %1$s";
+ public static final String MESSAGE_DUPLICATE_ROLE = "This client role already exists in the address book!";
+ private final String toAdd;
+
+ /**
+ * Creates an AddClientRoleCommand to add a new client role with the specified name.
+ *
+ * @param role The name of the new client role to be added.
+ */
+ public AddClientRoleCommand(String role) {
+ requireNonNull(role);
+ toAdd = role;
+ }
+
+ /**
+ * Executes the operation to add a new client role to the address book.
+ *
+ * @param model The model that contains the address book data.
+ * @return A CommandResult indicating the success or failure of the operation.
+ * @throws CommandException If an error occurs during the execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (ClientRoles.isValidRole(toAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_ROLE);
+ }
+
+ String successMessage = String.format(MESSAGE_SUCCESS, Messages.format(toAdd));
+ TabIndex index = TabIndex.Client;
+
+ ClientRoles newRole = new ClientRoles(toAdd.toString());
+ ClientRoles.addClientRole(newRole);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+ /**
+ * Checks if this AddClientRoleCommand is equal to another object.
+ *
+ * @param other The object to compare with this AddClientRoleCommand.
+ * @return True if the objects are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddDeveloperCommand)) {
+ return false;
+ }
+
+ AddClientRoleCommand otherAddClientRoleCommand = (AddClientRoleCommand) other;
+ return toAdd.equals(otherAddClientRoleCommand.toAdd);
+ }
+
+ /**
+ * Generates a string representation of this AddClientRoleCommand.
+ *
+ * @return A string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/addroles/AddDeveloperRoleCommand.java b/src/main/java/seedu/address/logic/commands/addroles/AddDeveloperRoleCommand.java
new file mode 100644
index 00000000000..a0e6d2b1a98
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/addroles/AddDeveloperRoleCommand.java
@@ -0,0 +1,97 @@
+package seedu.address.logic.commands.addroles;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.add.AddDeveloperCommand;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.developer.DeveloperRoles;
+
+/**
+ * Represents a command to add a new role for developers to the address book.
+ * This allows users to define custom roles for developers.
+ */
+public class AddDeveloperRoleCommand extends Command {
+ public static final String COMMAND_WORD = "add-developer-role";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a role for developers to the address book. "
+ + "Parameters: " + PREFIX_ROLE + "ROLE "
+ + "Example: " + PREFIX_ROLE + "Developer ";
+
+ public static final String MESSAGE_SUCCESS = "New role for developer added: %1$s";
+ public static final String MESSAGE_DUPLICATE_ROLE = "This developer role already exists in the address book!";
+ private final String toAdd;
+
+ /**
+ * Creates an AddDeveloperRoleCommand to add a new developer role with the specified name.
+ *
+ * @param role The name of the new developer role to be added.
+ */
+ public AddDeveloperRoleCommand(String role) {
+ requireNonNull(role);
+ toAdd = role;
+ }
+
+ /**
+ * Executes the operation to add a new developer role to the address book.
+ *
+ * @param model The model that contains the address book data.
+ * @return A CommandResult indicating the success or failure of the operation.
+ * @throws CommandException If an error occurs during the execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (DeveloperRoles.isValidRole(toAdd)) {
+ throw new CommandException(MESSAGE_DUPLICATE_ROLE);
+ }
+
+ String successMessage = String.format(MESSAGE_SUCCESS, Messages.format(toAdd));
+ TabIndex index = TabIndex.Developer;
+
+ DeveloperRoles newRole = new DeveloperRoles(toAdd.toString());
+ DeveloperRoles.addDeveloperRole(newRole);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+ /**
+ * Checks if this AddDeveloperRoleCommand is equal to another object.
+ *
+ * @param other The object to compare with this AddDeveloperRoleCommand.
+ * @return True if the objects are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddDeveloperCommand)) {
+ return false;
+ }
+
+ AddDeveloperRoleCommand otherAddDeveloperRoleCommand = (AddDeveloperRoleCommand) other;
+ return toAdd.equals(otherAddDeveloperRoleCommand.toAdd);
+ }
+
+ /**
+ * Generates a string representation of this AddDeveloperRoleCommand.
+ *
+ * @return A string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/delete/DeleteClientCommand.java b/src/main/java/seedu/address/logic/commands/delete/DeleteClientCommand.java
new file mode 100644
index 00000000000..bc18b4a29f8
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/delete/DeleteClientCommand.java
@@ -0,0 +1,76 @@
+package seedu.address.logic.commands.delete;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.client.Client;
+
+/**
+ * Deletes a developer identified using it's displayed index from the address book.
+ */
+public class DeleteClientCommand extends Command {
+
+ public static final String COMMAND_WORD = "delete-client";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the client identified by the index number used in the displayed client list.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_DELETE_CLIENT_SUCCESS = "Deleted Client: %1$s";
+
+ private final Index targetIndex;
+
+ public DeleteClientCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredClientList();
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX);
+ }
+
+ Client clientToDelete = lastShownList.get(targetIndex.getZeroBased());
+ String successMessage = String.format(MESSAGE_DELETE_CLIENT_SUCCESS, Messages.format(clientToDelete));
+ TabIndex index = TabIndex.Client;
+
+ model.deleteClient(clientToDelete);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteClientCommand)) {
+ return false;
+ }
+
+ DeleteClientCommand otherDeleteClientCommand = (DeleteClientCommand) other;
+ return targetIndex.equals(otherDeleteClientCommand.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/delete/DeleteDeveloperCommand.java b/src/main/java/seedu/address/logic/commands/delete/DeleteDeveloperCommand.java
new file mode 100644
index 00000000000..c78f310b526
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/delete/DeleteDeveloperCommand.java
@@ -0,0 +1,76 @@
+package seedu.address.logic.commands.delete;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.developer.Developer;
+
+/**
+ * Deletes a developer identified using it's displayed index from the address book.
+ */
+public class DeleteDeveloperCommand extends Command {
+
+ public static final String COMMAND_WORD = "delete-developer";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the developer identified by the index number used in the displayed developer list.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_DELETE_DEVELOPER_SUCCESS = "Deleted Developer: %1$s";
+
+ private final Index targetIndex;
+
+ public DeleteDeveloperCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredDeveloperList();
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_DEVELOPER_DISPLAYED_INDEX);
+ }
+
+ Developer developerToDelete = lastShownList.get(targetIndex.getZeroBased());
+ String successMessage = String.format(MESSAGE_DELETE_DEVELOPER_SUCCESS, Messages.format(developerToDelete));
+ TabIndex index = TabIndex.Developer;
+
+ model.deleteDeveloper(developerToDelete);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteDeveloperCommand)) {
+ return false;
+ }
+
+ DeleteDeveloperCommand otherDeleteDeveloperCommand = (DeleteDeveloperCommand) other;
+ return targetIndex.equals(otherDeleteDeveloperCommand.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/delete/DeleteProjectCommand.java b/src/main/java/seedu/address/logic/commands/delete/DeleteProjectCommand.java
new file mode 100644
index 00000000000..6bc0dca3ebd
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/delete/DeleteProjectCommand.java
@@ -0,0 +1,75 @@
+package seedu.address.logic.commands.delete;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.project.Project;
+
+/**
+ * Deletes a project identified using it's displayed index from the address book.
+ */
+public class DeleteProjectCommand extends Command {
+
+ public static final String COMMAND_WORD = "delete-project";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the project identified by the index number used in the displayed project list.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_DELETE_PROJECT_SUCCESS = "Deleted Project: %1$s";
+
+ private final Index targetIndex;
+
+ public DeleteProjectCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredProjectList();
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_DEVELOPER_DISPLAYED_INDEX);
+ }
+
+ Project projectToDelete = lastShownList.get(targetIndex.getZeroBased());
+ String successMessage = String.format(MESSAGE_DELETE_PROJECT_SUCCESS, Messages.format(projectToDelete));
+ TabIndex index = TabIndex.Project;
+ model.deleteProject(projectToDelete);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteProjectCommand)) {
+ return false;
+ }
+
+ DeleteProjectCommand otherDeleteDeveloperCommand = (DeleteProjectCommand) other;
+ return targetIndex.equals(otherDeleteDeveloperCommand.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/deleteroles/DeleteClientRoleCommand.java b/src/main/java/seedu/address/logic/commands/deleteroles/DeleteClientRoleCommand.java
new file mode 100644
index 00000000000..08bee5ea90f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/deleteroles/DeleteClientRoleCommand.java
@@ -0,0 +1,109 @@
+package seedu.address.logic.commands.deleteroles;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.delete.DeleteClientCommand;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.client.ClientRoles;
+
+/**
+ * Represents a command to delete a role for clients from the address book.
+ * This allows users to remove custom roles for clients.
+ */
+public class DeleteClientRoleCommand extends Command {
+ public static final String COMMAND_WORD = "delete-client-role";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Delete a role for clients in the address book. "
+ + "Parameters: " + PREFIX_ROLE + "ROLE "
+ + "Example: " + PREFIX_ROLE + "Developer ";
+
+ public static final String MESSAGE_SUCCESS = "Role for clients deleted: %1$s";
+ public static final String MESSAGE_CANNOT_DELETE_REPEAT = "This client role cannot be deleted "
+ + "as there are clients of this role";
+ public static final String MESSAGE_CANNOT_DELETE_PREXISTS = "You are not allowed to delete this client role.";
+ public static final String MESSAGE_CANNOT_DELETE_NONEXISTING = "This client role does not exist. ";
+ public static final String MESSAGE_EXISTING_CLIENT_ROLES = "These are the existing client roles: \n";
+
+ private final String toAdd;
+
+ /**
+ * Creates a DeleteClientRoleCommand to delete a specific client role by its name.
+ *
+ * @param role The name of the client role to be deleted.
+ */
+ public DeleteClientRoleCommand(String role) {
+ requireNonNull(role);
+ toAdd = role;
+ }
+
+ /**
+ * Executes the operation to delete a client role from the address book.
+ *
+ * @param model The model that contains the address book data.
+ * @return A CommandResult indicating the success or failure of the operation.
+ * @throws CommandException If an error occurs during the execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (ClientRoles.isRemovableRole(model, toAdd)) { //if when you search for the role result != 1
+ if (ClientRoles.isNotInList()) {
+ throw new CommandException(MESSAGE_CANNOT_DELETE_NONEXISTING + MESSAGE_EXISTING_CLIENT_ROLES
+ + ClientRoles.printRoles());
+ } else if (!ClientRoles.isNotDefault()) {
+ throw new CommandException(MESSAGE_CANNOT_DELETE_PREXISTS);
+ } else if (!ClientRoles.isNoRepeat()) {
+ throw new CommandException(MESSAGE_CANNOT_DELETE_REPEAT);
+ }
+ }
+
+ String successMessage = String.format(MESSAGE_SUCCESS, Messages.format(toAdd));
+ TabIndex index = TabIndex.Client;
+
+ ClientRoles newRole = new ClientRoles(toAdd.toString());
+ ClientRoles.deleteClientRole(newRole);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+ /**
+ * Checks if this DeleteClientRoleCommand is equal to another object.
+ *
+ * @param other The object to compare with this DeleteClientRoleCommand.
+ * @return True if the objects are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteClientCommand)) {
+ return false;
+ }
+
+ DeleteClientRoleCommand otherDeleteClientRoleCommand = (DeleteClientRoleCommand) other;
+ return toAdd.equals(otherDeleteClientRoleCommand.toAdd);
+ }
+
+ /**
+ * Generates a string representation of this DeleteClientRoleCommand.
+ *
+ * @return A string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/deleteroles/DeleteDeveloperRoleCommand.java b/src/main/java/seedu/address/logic/commands/deleteroles/DeleteDeveloperRoleCommand.java
new file mode 100644
index 00000000000..782ff350312
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/deleteroles/DeleteDeveloperRoleCommand.java
@@ -0,0 +1,109 @@
+package seedu.address.logic.commands.deleteroles;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.delete.DeleteClientCommand;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.developer.DeveloperRoles;
+
+/**
+ * Represents a command to delete a role for developers from the address book.
+ * This allows users to remove custom roles for developers.
+ */
+public class DeleteDeveloperRoleCommand extends Command {
+ public static final String COMMAND_WORD = "delete-developer-role";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Delete a role for developers in the address book. "
+ + "Parameters: " + PREFIX_ROLE + "ROLE "
+ + "Example: " + PREFIX_ROLE + "Developer ";
+
+ public static final String MESSAGE_SUCCESS = "Role for developers deleted: %1$s";
+ public static final String MESSAGE_CANNOT_DELETE_REPEAT = "This developer role cannot be deleted "
+ + "as there are developers of this role";
+ public static final String MESSAGE_CANNOT_DELETE_PREXISTS = "You are not allowed to delete this developer role.";
+ public static final String MESSAGE_CANNOT_DELETE_NONEXISTING = "This developer role does not exist. ";
+ public static final String MESSAGE_EXISTING_DEVELOPERS_ROLES = "These are the existing developer roles: \n";
+
+ private final String toAdd;
+
+ /**
+ * Creates a DeleteDeveloperRoleCommand to delete a specific developer role by its name.
+ *
+ * @param role The name of the developer role to be deleted.
+ */
+ public DeleteDeveloperRoleCommand(String role) {
+ requireNonNull(role);
+ toAdd = role;
+ }
+
+ /**
+ * Executes the operation to delete a developer role from the address book.
+ *
+ * @param model The model that contains the address book data.
+ * @return A CommandResult indicating the success or failure of the operation.
+ * @throws CommandException If an error occurs during the execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ if (DeveloperRoles.isRemovableRole(model, toAdd)) {
+ if (DeveloperRoles.isNotInList()) {
+ throw new CommandException(MESSAGE_CANNOT_DELETE_NONEXISTING + MESSAGE_EXISTING_DEVELOPERS_ROLES
+ + DeveloperRoles.printRoles());
+ } else if (!DeveloperRoles.isNotDefault()) {
+ throw new CommandException(MESSAGE_CANNOT_DELETE_PREXISTS);
+ } else if (!DeveloperRoles.isNoRepeat()) {
+ throw new CommandException(MESSAGE_CANNOT_DELETE_REPEAT);
+ }
+ }
+
+ String successMessage = String.format(MESSAGE_SUCCESS, Messages.format(toAdd));
+ TabIndex index = TabIndex.Developer;
+
+ DeveloperRoles newRole = new DeveloperRoles(toAdd.toString());
+ DeveloperRoles.deleteDeveloperRole(newRole);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+ /**
+ * Checks if this DeleteDeveloperRoleCommand is equal to another object.
+ *
+ * @param other The object to compare with this DeleteDeveloperRoleCommand.
+ * @return True if the objects are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteClientCommand)) {
+ return false;
+ }
+
+ DeleteDeveloperRoleCommand otherDeleteDeveloperRoleCommand = (DeleteDeveloperRoleCommand) other;
+ return toAdd.equals(otherDeleteDeveloperRoleCommand.toAdd);
+ }
+
+ /**
+ * Generates a string representation of this DeleteDeveloperRoleCommand.
+ *
+ * @return A string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/edit/EditClientCommand.java b/src/main/java/seedu/address/logic/commands/edit/EditClientCommand.java
new file mode 100644
index 00000000000..6369e959475
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/edit/EditClientCommand.java
@@ -0,0 +1,295 @@
+package seedu.address.logic.commands.edit;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOCUMENT;
+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_ORGANISATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.CollectionUtil;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.client.Client;
+import seedu.address.model.client.ClientRoles;
+import seedu.address.model.client.Document;
+import seedu.address.model.commons.Name;
+import seedu.address.model.person.Address;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Phone;
+
+/**
+ * Edits the details of an existing client in the address book.
+ */
+public class EditClientCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit-client";
+ public static final String MESSAGE_EDIT_CLIENT_SUCCESS = "Edited Client: %1$s";
+ public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
+ public static final String MESSAGE_DUPLICATE_CLIENT =
+ "There is already a client with that name!";
+ public static final String MESSAGE_UNEDITED_CLIENT =
+ "The details of the client to edit are already as such!";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the client identified "
+ + "by the index number used in the displayed client 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_ROLE + "ROLE] "
+ + "[" + PREFIX_PROJECT + "PROJECT]... "
+ + "[" + PREFIX_ORGANISATION + "ORGANISATION] "
+ + "[" + PREFIX_DOCUMENT + "DOCUMENT] \n"
+ + "Example: \n" + COMMAND_WORD + " 1 "
+ + PREFIX_PHONE + "91234567 "
+ + PREFIX_EMAIL + "johndoe@example.com";
+
+ private final Index index;
+
+ private final EditClientDescriptor editClientDescriptor;
+
+
+ /**
+ * @param index of the client in the filtered client list to edit
+ * @param editClientDescriptor details to edit the client with
+ */
+ public EditClientCommand(Index index, EditClientDescriptor editClientDescriptor) {
+ requireNonNull(index);
+ requireNonNull(editClientDescriptor);
+
+ this.index = index;
+ this.editClientDescriptor = new EditClientDescriptor(editClientDescriptor);
+ }
+
+ /**
+ * Creates and returns a {@code Client} with the details of {@code clientToEdit}
+ * edited with {@code editClientDescriptor}.
+ */
+ static Client createEditedClient(Client clientToEdit, EditClientDescriptor editClientDescriptor) {
+ assert clientToEdit != null;
+
+ Name updatedName = editClientDescriptor.getName().orElse(clientToEdit.getName());
+ Phone updatedPhone = editClientDescriptor.getPhone().orElse(clientToEdit.getPhone());
+ Email updatedEmail = editClientDescriptor.getEmail().orElse(clientToEdit.getEmail());
+ Address updatedAddress = editClientDescriptor.getAddress().orElse(clientToEdit.getAddress());
+ ClientRoles updatedRole = editClientDescriptor.getRole().orElse(clientToEdit.getRole());
+ Set updatedProjects = editClientDescriptor.getProjects().orElse(clientToEdit.getProjects());
+ Name updatedOrganisation = editClientDescriptor.getOrganisation().orElse(clientToEdit.getOrganisation());
+ Document updatedDocument = editClientDescriptor.getDocument().orElse(clientToEdit.getDocument());
+
+ return new Client(updatedName, updatedPhone, updatedEmail, updatedAddress,
+ updatedRole, updatedProjects, updatedOrganisation, updatedDocument);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredClientList();
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_CLIENT_DISPLAYED_INDEX);
+ }
+
+ Client clientToEdit = lastShownList.get(index.getZeroBased());
+ Client editedClient = createEditedClient(clientToEdit, editClientDescriptor);
+
+ if (!clientToEdit.isSameClient(editedClient) && model.hasClient(editedClient)) {
+ throw new CommandException(MESSAGE_DUPLICATE_CLIENT);
+ }
+ String res = model.areProjectsValid(editedClient);
+ if (res != null) {
+ throw new CommandException(String.format(Messages.MESSAGE_NONEXISTENT_PROJECT, res));
+ }
+
+ String successMessage = String.format(MESSAGE_EDIT_CLIENT_SUCCESS, Messages.format(editedClient));
+ TabIndex index = TabIndex.Client;
+
+ model.setClient(clientToEdit, editedClient);
+ model.updateFilteredClientList(Model.PREDICATE_SHOW_ALL_CLIENTS);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditClientCommand)) {
+ return false;
+ }
+
+ EditClientCommand otherEditClientCommand = (EditClientCommand) other;
+ return index.equals(otherEditClientCommand.index)
+ && editClientDescriptor.equals(otherEditClientCommand.editClientDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", index)
+ .add("editClientDescriptor", editClientDescriptor)
+ .toString();
+ }
+
+ /**
+ * Stores the details to edit the client with. Each non-empty field value will replace the
+ * corresponding field value of the client.
+ */
+ public static class EditClientDescriptor {
+ private Name name;
+ private Phone phone;
+ private Email email;
+ private Address address;
+ private Set projects;
+ private ClientRoles role;
+ private Name organisation;
+ private Document document;
+
+ public EditClientDescriptor() {
+ }
+
+ /**
+ * Copy constructor.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public EditClientDescriptor(EditClientDescriptor toCopy) {
+ setName(toCopy.name);
+ setPhone(toCopy.phone);
+ setEmail(toCopy.email);
+ setAddress(toCopy.address);
+ setProjects(toCopy.projects);
+ setRole(toCopy.role);
+ setOrganisation(toCopy.organisation);
+ setDocument(toCopy.document);
+ }
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldEdited() {
+ return CollectionUtil.isAnyNonNull(
+ name, phone, email, address, projects, role, organisation, document);
+ }
+
+ public Optional getName() {
+ return Optional.ofNullable(name);
+ }
+
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+ public Optional getPhone() {
+ return Optional.ofNullable(phone);
+ }
+
+ public void setPhone(Phone phone) {
+ this.phone = phone;
+ }
+
+ public Optional getEmail() {
+ return Optional.ofNullable(email);
+ }
+
+ public void setEmail(Email email) {
+ this.email = email;
+ }
+
+ public Optional getAddress() {
+ return Optional.ofNullable(address);
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public Optional> getProjects() {
+ return (projects != null) ? Optional.of(Collections.unmodifiableSet(projects)) : Optional.empty();
+ }
+
+ public void setProjects(Set projects) {
+ this.projects = (projects != null) ? new HashSet<>(projects) : null;
+ }
+
+ public Optional getOrganisation() {
+ return Optional.ofNullable(organisation);
+ }
+
+ public void setOrganisation(Name organisation) {
+ this.organisation = organisation;
+ }
+
+ public Optional getDocument() {
+ return Optional.ofNullable(document);
+ }
+
+ public void setDocument(Document document) {
+ this.document = document;
+ }
+
+ public Optional getRole() {
+ return Optional.ofNullable(role);
+ }
+
+ public void setRole(ClientRoles role) {
+ this.role = role;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof EditClientDescriptor)) {
+ return false;
+ }
+
+ EditClientDescriptor otherEditClientDescriptor = (EditClientDescriptor) other;
+ return Objects.equals(name, otherEditClientDescriptor.name)
+ && Objects.equals(phone, otherEditClientDescriptor.phone)
+ && Objects.equals(email, otherEditClientDescriptor.email)
+ && Objects.equals(address, otherEditClientDescriptor.address)
+ && Objects.equals(projects, otherEditClientDescriptor.projects)
+ && Objects.equals(role, otherEditClientDescriptor.role)
+ && Objects.equals(organisation, otherEditClientDescriptor.organisation)
+ && Objects.equals(document, otherEditClientDescriptor.document);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("name", name)
+ .add("phone", phone)
+ .add("email", email)
+ .add("address", address)
+ .add("projects", projects)
+ .add("role", role)
+ .add("organisation", organisation)
+ .add("document", document)
+ .toString();
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/edit/EditDeveloperCommand.java b/src/main/java/seedu/address/logic/commands/edit/EditDeveloperCommand.java
new file mode 100644
index 00000000000..0bcbf4819b5
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/edit/EditDeveloperCommand.java
@@ -0,0 +1,326 @@
+package seedu.address.logic.commands.edit;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GITHUBID;
+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_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.CollectionUtil;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.commons.Date;
+import seedu.address.model.commons.Name;
+import seedu.address.model.developer.Developer;
+import seedu.address.model.developer.DeveloperRoles;
+import seedu.address.model.developer.GithubId;
+import seedu.address.model.developer.Rating;
+import seedu.address.model.developer.Salary;
+import seedu.address.model.person.Address;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Phone;
+
+/**
+ * Edits the details of an existing developer in the address book.
+ */
+public class EditDeveloperCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit-developer";
+ public static final String MESSAGE_EDIT_DEVELOPER_SUCCESS = "Edited Developer: %1$s";
+ public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
+ public static final String MESSAGE_DUPLICATE_DEVELOPER =
+ "There is already a developer with that name!";
+ public static final String MESSAGE_UNEDITED_DEVELOPER =
+ "The details of the developer to edit are already as such!";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the developer identified "
+ + "by the index number used in the displayed developer 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_ROLE + "ROLE] "
+ + "[" + PREFIX_PROJECT + "PROJECT]... "
+ + "[" + PREFIX_SALARY + "SALARY] "
+ + "[" + PREFIX_DATEJOINED + "DATE JOINED] "
+ + "[" + PREFIX_GITHUBID + "GITHUBID] "
+ + "[" + PREFIX_RATING + "RATING] \n"
+ + "Example: \n" + COMMAND_WORD + " 1 "
+ + PREFIX_PHONE + "91234567 "
+ + PREFIX_EMAIL + "johndoe@example.com";
+
+ private final Index index;
+
+ private final EditDeveloperDescriptor editDeveloperDescriptor;
+
+
+ /**
+ * @param index of the developer in the filtered developer list to edit
+ * @param editDeveloperDescriptor details to edit the developer with
+ */
+ public EditDeveloperCommand(Index index, EditDeveloperDescriptor editDeveloperDescriptor) {
+ requireNonNull(index);
+ requireNonNull(editDeveloperDescriptor);
+
+ this.index = index;
+ this.editDeveloperDescriptor = new EditDeveloperDescriptor(editDeveloperDescriptor);
+ }
+
+ /**
+ * Creates and returns a {@code Developer} with the details of {@code developerToEdit}
+ * edited with {@code editDeveloperDescriptor}.
+ */
+ static Developer createEditedDeveloper(Developer developerToEdit, EditDeveloperDescriptor editDeveloperDescriptor) {
+ assert developerToEdit != null;
+
+ Name updatedName = editDeveloperDescriptor.getName().orElse(developerToEdit.getName());
+ Phone updatedPhone = editDeveloperDescriptor.getPhone().orElse(developerToEdit.getPhone());
+ Email updatedEmail = editDeveloperDescriptor.getEmail().orElse(developerToEdit.getEmail());
+ Address updatedAddress = editDeveloperDescriptor.getAddress().orElse(developerToEdit.getAddress());
+ Date updatedDateJoined = editDeveloperDescriptor.getDateJoined().orElse(developerToEdit.getDateJoined());
+ DeveloperRoles updatedRole = editDeveloperDescriptor.getRole().orElse(developerToEdit.getRole());
+ Salary updatedSalary = editDeveloperDescriptor.getSalary().orElse(developerToEdit.getSalary());
+ Set updatedProjects = editDeveloperDescriptor.getProjects().orElse(developerToEdit.getProjects());
+ GithubId updatedGithubId = editDeveloperDescriptor.getGithubId().orElse(developerToEdit.getGithubId());
+ Rating updatedRating = editDeveloperDescriptor.getRating().orElse(developerToEdit.getRating());
+
+ return new Developer(updatedName, updatedPhone, updatedEmail, updatedAddress,
+ updatedRole, updatedProjects, updatedSalary, updatedDateJoined, updatedGithubId, updatedRating);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredDeveloperList();
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_DEVELOPER_DISPLAYED_INDEX);
+ }
+
+ Developer developerToEdit = lastShownList.get(index.getZeroBased());
+ Developer editedDeveloper = createEditedDeveloper(developerToEdit, editDeveloperDescriptor);
+
+ if (!developerToEdit.isSameDeveloper(editedDeveloper) && model.hasDeveloper(editedDeveloper)) {
+ throw new CommandException(MESSAGE_DUPLICATE_DEVELOPER);
+ }
+ String res = model.areProjectsValid(editedDeveloper);
+ if (res != null) {
+ throw new CommandException(String.format(Messages.MESSAGE_NONEXISTENT_PROJECT, res));
+ }
+
+ String successMessage = String.format(MESSAGE_EDIT_DEVELOPER_SUCCESS, Messages.format(editedDeveloper));
+ TabIndex index = TabIndex.Developer;
+
+ model.setDeveloper(developerToEdit, editedDeveloper);
+ model.updateFilteredDeveloperList(Model.PREDICATE_SHOW_ALL_DEVELOPERS);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditDeveloperCommand)) {
+ return false;
+ }
+
+ EditDeveloperCommand otherEditDeveloperCommand = (EditDeveloperCommand) other;
+ return index.equals(otherEditDeveloperCommand.index)
+ && editDeveloperDescriptor.equals(otherEditDeveloperCommand.editDeveloperDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", index)
+ .add("editDeveloperDescriptor", editDeveloperDescriptor)
+ .toString();
+ }
+
+ /**
+ * Stores the details to edit the developer with. Each non-empty field value will replace the
+ * corresponding field value of the developer.
+ */
+ public static class EditDeveloperDescriptor {
+ private Name name;
+ private Phone phone;
+ private Email email;
+ private Address address;
+ private Set projects;
+ private Date dateJoined;
+ private DeveloperRoles role;
+ private Salary salary;
+ private GithubId githubId;
+ private Rating rating;
+
+ public EditDeveloperDescriptor() {
+ }
+
+ /**
+ * Copy constructor.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public EditDeveloperDescriptor(EditDeveloperDescriptor toCopy) {
+ setName(toCopy.name);
+ setPhone(toCopy.phone);
+ setEmail(toCopy.email);
+ setAddress(toCopy.address);
+ setProjects(toCopy.projects);
+ setDateJoined(toCopy.dateJoined);
+ setRole(toCopy.role);
+ setSalary(toCopy.salary);
+ setGithubId(toCopy.githubId);
+ setRating(toCopy.rating);
+ }
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldEdited() {
+ return CollectionUtil.isAnyNonNull(
+ name, phone, email, address, projects, dateJoined, role, salary, githubId, rating);
+ }
+
+ public Optional getName() {
+ return Optional.ofNullable(name);
+ }
+
+ public void setName(Name name) {
+ this.name = name;
+ }
+
+ public Optional getPhone() {
+ return Optional.ofNullable(phone);
+ }
+
+ public void setPhone(Phone phone) {
+ this.phone = phone;
+ }
+
+ public Optional getEmail() {
+ return Optional.ofNullable(email);
+ }
+
+ public void setEmail(Email email) {
+ this.email = email;
+ }
+
+ public Optional getAddress() {
+ return Optional.ofNullable(address);
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public Optional> getProjects() {
+ return (projects != null) ? Optional.of(Collections.unmodifiableSet(projects)) : Optional.empty();
+ }
+
+ public void setProjects(Set projects) {
+ this.projects = (projects != null) ? new HashSet<>(projects) : null;
+ }
+
+ public Optional getDateJoined() {
+ return Optional.ofNullable(dateJoined);
+ }
+
+ public void setDateJoined(Date dateJoined) {
+ this.dateJoined = dateJoined;
+ }
+
+ public Optional getRole() {
+ return Optional.ofNullable(role);
+ }
+
+ public void setRole(DeveloperRoles role) {
+ this.role = role;
+ }
+
+ public Optional getSalary() {
+ return Optional.ofNullable(salary);
+ }
+
+ public void setSalary(Salary salary) {
+ this.salary = salary;
+ }
+
+ public Optional getGithubId() {
+ return Optional.ofNullable(githubId);
+ }
+
+ public void setGithubId(GithubId githubId) {
+ this.githubId = githubId;
+ }
+
+ public Optional getRating() {
+ return Optional.ofNullable(rating);
+ }
+
+ public void setRating(Rating rating) {
+ this.rating = rating;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof EditDeveloperDescriptor)) {
+ return false;
+ }
+
+ EditDeveloperDescriptor otherEditDeveloperDescriptor = (EditDeveloperDescriptor) other;
+ return Objects.equals(name, otherEditDeveloperDescriptor.name)
+ && Objects.equals(phone, otherEditDeveloperDescriptor.phone)
+ && Objects.equals(email, otherEditDeveloperDescriptor.email)
+ && Objects.equals(address, otherEditDeveloperDescriptor.address)
+ && Objects.equals(projects, otherEditDeveloperDescriptor.projects)
+ && Objects.equals(dateJoined, otherEditDeveloperDescriptor.dateJoined)
+ && Objects.equals(role, otherEditDeveloperDescriptor.role)
+ && Objects.equals(salary, otherEditDeveloperDescriptor.salary);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("name", name)
+ .add("phone", phone)
+ .add("email", email)
+ .add("address", address)
+ .add("projects", projects)
+ .add("dateJoined", dateJoined)
+ .add("role", role)
+ .add("salary", salary)
+ .add("githubId", githubId)
+ .add("rating", rating)
+ .toString();
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/edit/EditProjectCommand.java b/src/main/java/seedu/address/logic/commands/edit/EditProjectCommand.java
new file mode 100644
index 00000000000..f05dd605ba8
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/edit/EditProjectCommand.java
@@ -0,0 +1,201 @@
+package seedu.address.logic.commands.edit;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.CollectionUtil;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.commons.Name;
+import seedu.address.model.project.Deadline;
+import seedu.address.model.project.Description;
+import seedu.address.model.project.Project;
+
+/**
+ * Edits the details of an existing project in the address book.
+ */
+public class EditProjectCommand extends Command {
+
+ public static final String COMMAND_WORD = "edit-project";
+ public static final String MESSAGE_EDIT_PROJECT_SUCCESS = "Edited Project: %1$s";
+ public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided.";
+ public static final String MESSAGE_DUPLICATE_PROJECT =
+ "There is already a project with that name!";
+ public static final String MESSAGE_UNEDITED_PROJECT =
+ "The details of the project to edit are already as such!";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the project identified "
+ + "by the index number used in the displayed project list. "
+ + "Existing values will be overwritten by the input values.\n"
+ + Deadline.MESSAGE_CONSTRAINTS + "\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + "[" + PREFIX_DESCRIPTION + "DESCRIPTION] "
+ + "[" + PREFIX_DEADLINE + "DEADLINE_DATE,DEADLINE_DESCRIPTION,PRIORITY,IS_DONE]...\n"
+ + "Example: \n" + COMMAND_WORD + " 1 "
+ + PREFIX_DEADLINE + "31-12-2019,Develop front end interface,HIGH,0 "
+ + PREFIX_DEADLINE + "01-02-2020,Develop back end,HIGH,0";
+
+ private final Index index;
+
+ private final EditProjectDescriptor editProjectDescriptor;
+
+
+ /**
+ * @param index of the project in the filtered project list to edit
+ * @param editProjectDescriptor details to edit the project with
+ */
+ public EditProjectCommand(Index index, EditProjectDescriptor editProjectDescriptor) {
+ requireNonNull(index);
+ requireNonNull(editProjectDescriptor);
+
+ this.index = index;
+ this.editProjectDescriptor = new EditProjectDescriptor(editProjectDescriptor);
+ }
+
+ /**
+ * Creates and returns a {@code Project} with the details of {@code projectToEdit}
+ * edited with {@code editProjectDescriptor}.
+ */
+ static seedu.address.model.project.Project createEditedProject(seedu.address.model.project.Project projectToEdit,
+ EditProjectDescriptor editProjectDescriptor) {
+ assert projectToEdit != null;
+
+ Name name = projectToEdit.getProjectName();
+ Description updatedDescription = editProjectDescriptor.getDescription()
+ .orElse(projectToEdit.getProjectDescription());
+ List updatedDeadlines = editProjectDescriptor.getDeadlines()
+ .orElse(projectToEdit.getProjectDeadlines());
+
+ return new seedu.address.model.project.Project(name, updatedDescription, updatedDeadlines);
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredProjectList();
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PROJECT_DISPLAYED_INDEX);
+ }
+
+ Project projectToEdit = lastShownList.get(index.getZeroBased());
+ Project editedProject = createEditedProject(projectToEdit, editProjectDescriptor);
+
+ if (!projectToEdit.isSameProject(editedProject) && model.hasProject(editedProject)) {
+ throw new CommandException(MESSAGE_DUPLICATE_PROJECT);
+ }
+
+ String successMessage = String.format(MESSAGE_EDIT_PROJECT_SUCCESS, Messages.format(editedProject));
+ TabIndex index = TabIndex.Project;
+
+ model.setProject(projectToEdit, editedProject);
+ model.updateFilteredProjectList(Model.PREDICATE_SHOW_ALL_PROJECTS);
+ model.commitAddressBook(model, successMessage, index);
+ return new CommandResult(successMessage, index);
+ }
+
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EditProjectCommand)) {
+ return false;
+ }
+
+ EditProjectCommand otherEditProjectCommand = (EditProjectCommand) other;
+ return index.equals(otherEditProjectCommand.index)
+ && editProjectDescriptor.equals(otherEditProjectCommand.editProjectDescriptor);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("index", index)
+ .add("editProjectDescriptor", editProjectDescriptor)
+ .toString();
+ }
+
+ /**
+ * Stores the details to edit the project with. Each non-empty field value will replace the
+ * corresponding field value of the project.
+ */
+ public static class EditProjectDescriptor {
+ private Description desc;
+ private List deadlines;
+
+ public EditProjectDescriptor() {
+ }
+
+ /**
+ * Copy constructor.
+ * A defensive copy of {@code tags} is used internally.
+ */
+ public EditProjectDescriptor(EditProjectDescriptor toCopy) {
+ setDescription(toCopy.desc);
+ setDeadlines(toCopy.deadlines);
+ }
+
+ /**
+ * Returns true if at least one field is edited.
+ */
+ public boolean isAnyFieldEdited() {
+ return CollectionUtil.isAnyNonNull(
+ desc, deadlines);
+ }
+
+ public Optional getDescription() {
+ return Optional.ofNullable(desc);
+ }
+
+ public void setDescription(Description desc) {
+ this.desc = desc;
+ }
+
+ public Optional> getDeadlines() {
+ return (deadlines != null) ? Optional.of(Collections.unmodifiableList(deadlines)) : Optional.empty();
+ }
+
+ public void setDeadlines(List deadlines) {
+ this.deadlines = (deadlines != null) ? new ArrayList<>(deadlines) : null;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof EditProjectDescriptor)) {
+ return false;
+ }
+
+ EditProjectDescriptor otherEditProjectDescriptor = (EditProjectDescriptor) other;
+ return Objects.equals(desc, otherEditProjectDescriptor.desc)
+ && Objects.equals(deadlines, otherEditProjectDescriptor.deadlines);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("description", desc)
+ .add("deadlines", deadlines)
+ .toString();
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/find/FindClientCommand.java b/src/main/java/seedu/address/logic/commands/find/FindClientCommand.java
new file mode 100644
index 00000000000..860f6230dfa
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/find/FindClientCommand.java
@@ -0,0 +1,100 @@
+package seedu.address.logic.commands.find;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.getMessageClientsListedOverview;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOCUMENT;
+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_ORGANISATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.model.Model;
+import seedu.address.model.client.Client;
+
+/**
+ * Represents a command to find clients based on various attributes.
+ * This command allows users to search for clients by specifying keywords for different client attributes.
+ */
+public class FindClientCommand extends Command {
+ public static final String COMMAND_WORD = "find-client";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Find clients based on various attributes.\n"
+ + "Parameters: "
+ + "[" + PREFIX_NAME + "NAME_KEYWORDS] "
+ + "[" + PREFIX_ROLE + "ROLE_KEYWORDS] "
+ + "[" + PREFIX_ADDRESS + "ADDRESS_KEYWORDS] "
+ + "[" + PREFIX_EMAIL + "EMAIL_KEYWORDS] "
+ + "[" + PREFIX_PHONE + "PHONE_KEYWORDS] "
+ + "[" + PREFIX_PROJECT + "PROJECT_KEYWORDS] "
+ + "[" + PREFIX_DOCUMENT + "DOCUMENT_KEYWORDS] "
+ + "[" + PREFIX_ORGANISATION + "ORGANISATION_KEYWORDS]\n"
+ + "Example: " + COMMAND_WORD + " n/John r/client\n";
+
+ private Predicate predicate;
+
+ /**
+ * Creates a FindClientCommand with the specified predicate.
+ *
+ * @param predicate The predicate used to filter the list of clients.
+ */
+ public FindClientCommand(Predicate predicate) {
+ this.predicate = predicate;
+ }
+
+ /**
+ * Executes the find operation by updating the filtered client list based on the given predicate.
+ *
+ * @param model The model to execute the command on.
+ * @return A CommandResult indicating the results of the find operation.
+ */
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredClientList(predicate);
+
+ int resultCount = model.getFilteredClientList().size();
+ String message = getMessageClientsListedOverview(resultCount);
+
+ return new CommandResult(message, TabIndex.Client);
+ }
+
+ /**
+ * Checks if this FindClientCommand is equal to another object.
+ *
+ * @param other The object to compare with this FindClientCommand.
+ * @return True if the objects are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof FindClientCommand)) {
+ return false;
+ }
+
+ FindClientCommand otherFindClientCommand = (FindClientCommand) other;
+ return predicate.equals(otherFindClientCommand.predicate);
+ }
+
+ /**
+ * Generates a string representation of this FindClientCommand.
+ *
+ * @return A string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/find/FindDeadlineCommand.java b/src/main/java/seedu/address/logic/commands/find/FindDeadlineCommand.java
new file mode 100644
index 00000000000..517e0345235
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/find/FindDeadlineCommand.java
@@ -0,0 +1,93 @@
+package seedu.address.logic.commands.find;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.getMessageDeadlinesListedOverview;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PRIORITY;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.model.Model;
+import seedu.address.model.project.Deadline;
+
+/**
+ * Represents a command to filter deadlines within projects based on specified criteria.
+ * This command allows users to search for deadlines based on dates and priority levels.
+ */
+public class FindDeadlineCommand extends Command {
+ public static final String COMMAND_WORD = "find-deadline";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filter deadlines within projects.\n"
+ + "Parameters: "
+ + PREFIX_DATEJOINED + "DATE (Shows deadlines before this date) \n"
+ + "[" + PREFIX_PRIORITY + "[HIGH/MEDIUM/LOW] (Shows deadlines based on priority)\n"
+ + "Example: " + COMMAND_WORD + " d/28-12-2021 pri/HIGH\n";
+
+ private final Predicate predicate;
+
+ /**
+ * Creates a FindDeadlineCommand with the specified predicate for filtering deadlines.
+ *
+ * @param predicate The predicate used to filter deadlines within projects.
+ */
+ public FindDeadlineCommand(Predicate predicate) {
+ this.predicate = predicate;
+ }
+
+ /**
+ * Executes the find operation by updating the filtered project deadline list based on the given predicate.
+ *
+ * @param model The model to execute the command on.
+ * @return A CommandResult indicating the results of the find operation.
+ */
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+
+ model.updateFilteredProjectDeadlineList(predicate);
+
+ int resultCount =
+ model.getFilteredProjectList().stream()
+ .mapToInt(project -> project.getProjectFilteredDeadlines().size()).filter(x -> x > 0).sum();
+
+ String message = getMessageDeadlinesListedOverview(resultCount);
+
+ return new CommandResult(message, TabIndex.Project);
+ }
+
+ /**
+ * Checks if this FindDeadlineCommand is equal to another object.
+ *
+ * @param other The object to compare with this FindDeadlineCommand.
+ * @return True if the objects are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof FindDeadlineCommand)) {
+ return false;
+ }
+
+ FindDeadlineCommand otherFindDeadlineCommand = (FindDeadlineCommand) other;
+ return predicate.equals(otherFindDeadlineCommand.predicate);
+ }
+
+ /**
+ * Generates a string representation of this FindDeadlineCommand.
+ *
+ * @return A string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/find/FindDeveloperCommand.java b/src/main/java/seedu/address/logic/commands/find/FindDeveloperCommand.java
new file mode 100644
index 00000000000..595cc56b9c4
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/find/FindDeveloperCommand.java
@@ -0,0 +1,106 @@
+package seedu.address.logic.commands.find;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.getMessageDevelopersListedOverview;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GITHUBID;
+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_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.model.Model;
+import seedu.address.model.developer.Developer;
+
+/**
+ * Represents a command to find developers in the address book based on various attributes.
+ * This command allows users to search for developers using their names, roles, addresses, dates joined, emails,
+ * phone numbers, associated projects, salaries, ratings, or GitHub IDs.
+ */
+public class FindDeveloperCommand extends Command {
+
+ public static final String COMMAND_WORD = "find-developer";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Find developers based on various attributes.\n"
+ + "Parameters: "
+ + "[" + PREFIX_NAME + "NAME_KEYWORDS] "
+ + "[" + PREFIX_ROLE + "ROLE_KEYWORDS] "
+ + "[" + PREFIX_ADDRESS + "ADDRESS_KEYWORDS] "
+ + "[" + PREFIX_DATEJOINED + "DATE_JOINED_KEYWORDS] "
+ + "[" + PREFIX_EMAIL + "EMAIL_KEYWORDS] "
+ + "[" + PREFIX_PHONE + "PHONE_KEYWORDS] "
+ + "[" + PREFIX_PROJECT + "PROJECT_KEYWORDS] "
+ + "[" + PREFIX_SALARY + "SALARY_KEYWORDS] "
+ + "[" + PREFIX_RATING + "RATING_KEYWORDS] "
+ + "[" + PREFIX_GITHUBID + "GITHUBID_KEYWORDS]\n"
+ + "Example: " + COMMAND_WORD + " n/John r/developer\n";
+
+ private Predicate predicate;
+
+ /**
+ * Creates a FindDeveloperCommand with the specified predicate for filtering developers.
+ *
+ * @param predicate The predicate used to filter developers based on attributes.
+ */
+ public FindDeveloperCommand(Predicate predicate) {
+ this.predicate = predicate;
+ }
+
+ /**
+ * Executes the find operation by updating the filtered developer list based on the given predicate.
+ *
+ * @param model The model to execute the command on.
+ * @return A CommandResult indicating the results of the find operation.
+ */
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredDeveloperList(predicate);
+
+ int resultCount = model.getFilteredDeveloperList().size();
+ String message = getMessageDevelopersListedOverview(resultCount);
+
+ return new CommandResult(message, TabIndex.Developer);
+ }
+
+ /**
+ * Checks if this FindDeveloperCommand is equal to another object.
+ *
+ * @param other The object to compare with this FindDeveloperCommand.
+ * @return True if the objects are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof FindDeveloperCommand)) {
+ return false;
+ }
+
+ FindDeveloperCommand otherFindDeveloperCommand = (FindDeveloperCommand) other;
+ return predicate.equals(otherFindDeveloperCommand.predicate);
+ }
+
+ /**
+ * Generates a string representation of this FindDeveloperCommand.
+ *
+ * @return A string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/find/FindProjectCommand.java b/src/main/java/seedu/address/logic/commands/find/FindProjectCommand.java
new file mode 100644
index 00000000000..148dd78445c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/find/FindProjectCommand.java
@@ -0,0 +1,70 @@
+package seedu.address.logic.commands.find;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.getMessageProjectsListedOverview;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.model.Model;
+import seedu.address.model.project.Project;
+
+/**
+ * Finds and lists all persons in address book whose name contains any of the argument keywords.
+ * Keyword matching is case-insensitive.
+ */
+public class FindProjectCommand extends Command {
+
+ public static final String COMMAND_WORD = "find-project";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Find projects based on various attributes.\n"
+ + "Parameters: "
+ + "[" + PREFIX_PROJECT + "PROJECT_NAME_KEYWORDS] "
+ + "[" + PREFIX_DESCRIPTION + "DESCRIPTION_KEYWORDS] "
+ + "[" + PREFIX_DEADLINE + "DEADLINE_KEYWORDS]\n"
+ + "Example: " + COMMAND_WORD + " pr/MyProject\n";
+
+ private Predicate predicate;
+
+ public FindProjectCommand(Predicate predicate) {
+ this.predicate = predicate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredProjectList(predicate);
+
+ int resultCount = model.getFilteredProjectList().size();
+ String message = getMessageProjectsListedOverview(resultCount);
+
+ return new CommandResult(message, TabIndex.Project);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof FindProjectCommand)) {
+ return false;
+ }
+
+ FindProjectCommand otherFindProjectCommand = (FindProjectCommand) other;
+ return predicate.equals(otherFindProjectCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/imports/ImportClientCommand.java b/src/main/java/seedu/address/logic/commands/imports/ImportClientCommand.java
new file mode 100644
index 00000000000..8892e82eaff
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/imports/ImportClientCommand.java
@@ -0,0 +1,91 @@
+package seedu.address.logic.commands.imports;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.add.AddClientCommand;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.client.Client;
+
+/**
+ * Represents a command to import clients from a CSV file into the address book.
+ * This command allows users to add multiple clients from a CSV file with specific column titles.
+ */
+public class ImportClientCommand extends Command {
+ public static final String COMMAND_WORD = "import-client";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Imports clients from a CSV file.\n"
+ + "Column titles should follow this format strictly:\n"
+ + "Name, Contact Number, Email, Address, Role, Organisation, Document, Projects";
+ public static final String MESSAGE_SUCCESS = "New client added: %1$s";
+
+ private final ArrayList toAddList;
+
+ /**
+ * Creates an ImportClientCommand to import the specified list of clients.
+ *
+ * @param clientList The list of clients to be imported.
+ */
+ public ImportClientCommand(ArrayList clientList) {
+ requireNonNull(clientList);
+ for (Client i : clientList) {
+ requireNonNull(i);
+ }
+ toAddList = clientList;
+ }
+
+ /**
+ * Executes the import operation by adding clients from the CSV file to the address book.
+ *
+ * @param model The model in which to import the clients.
+ * @return A CommandResult indicating the success of the import operation.
+ * @throws CommandException If an error occurs during the import operation.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ String output = "";
+ for (Client toAdd : toAddList) {
+ CommandResult result = new AddClientCommand(toAdd).execute(model);
+ output += result.getFeedbackToUser() + "\n";
+ }
+ return new CommandResult(output, TabIndex.Client);
+ }
+
+ /**
+ * Checks if this ImportClientCommand is equal to another object.
+ *
+ * @param other The object to compare with this ImportClientCommand.
+ * @return True if the objects are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof ImportClientCommand)) {
+ return false;
+ }
+
+ ImportClientCommand otherImportCommand = (ImportClientCommand) other;
+ return toAddList.equals(otherImportCommand.toAddList);
+ }
+
+ /**
+ * Generates a string representation of this ImportClientCommand.
+ *
+ * @return A string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAddList", toAddList)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/imports/ImportDeveloperCommand.java b/src/main/java/seedu/address/logic/commands/imports/ImportDeveloperCommand.java
new file mode 100644
index 00000000000..378503e76ba
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/imports/ImportDeveloperCommand.java
@@ -0,0 +1,90 @@
+package seedu.address.logic.commands.imports;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.add.AddDeveloperCommand;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.developer.Developer;
+/**
+ * Represents a command to import developers from a CSV file into the address book.
+ * This command allows users to add multiple developers from a CSV file with specific column titles.
+ */
+public class ImportDeveloperCommand extends Command {
+ public static final String COMMAND_WORD = "import-developer";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Imports developers from a CSV file.\n"
+ + "Column titles should follow this format strictly:\n"
+ + "Name, Contact Number, Email, Address, Date Joined, Role, Salary, GithubId, Rating, Projects";
+ public static final String MESSAGE_SUCCESS = "New developer added: %1$s";
+
+ private final ArrayList toAddList;
+
+ /**
+ * Creates an ImportDeveloperCommand to import the specified list of developers.
+ *
+ * @param developerList The list of developers to be imported.
+ */
+ public ImportDeveloperCommand(ArrayList developerList) {
+ requireNonNull(developerList);
+ for (Developer i : developerList) {
+ requireNonNull(i);
+ }
+ toAddList = developerList;
+ }
+
+ /**
+ * Executes the import operation by adding developers from the CSV file to the address book.
+ *
+ * @param model The model in which to import the developers.
+ * @return A CommandResult indicating the success of the import operation.
+ * @throws CommandException If an error occurs during the import operation.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ String output = "";
+ for (Developer toAdd : toAddList) {
+ CommandResult result = new AddDeveloperCommand(toAdd).execute(model);
+ output += result.getFeedbackToUser() + "\n";
+ }
+ return new CommandResult(output, TabIndex.Developer);
+ }
+
+ /**
+ * Checks if this ImportDeveloperCommand is equal to another object.
+ *
+ * @param other The object to compare with this ImportDeveloperCommand.
+ * @return True if the objects are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof ImportDeveloperCommand)) {
+ return false;
+ }
+
+ ImportDeveloperCommand otherImportCommand = (ImportDeveloperCommand) other;
+ return toAddList.equals(otherImportCommand.toAddList);
+ }
+
+ /**
+ * Generates a string representation of this ImportDeveloperCommand.
+ *
+ * @return A string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAddList", toAddList)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/list/ListClientCommand.java b/src/main/java/seedu/address/logic/commands/list/ListClientCommand.java
new file mode 100644
index 00000000000..2351487cf9d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/list/ListClientCommand.java
@@ -0,0 +1,28 @@
+package seedu.address.logic.commands.list;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_CLIENTS;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.model.Model;
+
+/**
+ * Lists all persons in the address book to the user.
+ */
+public class ListClientCommand extends Command {
+
+ public static final String COMMAND_WORD = "list-client";
+
+ public static final String MESSAGE_SUCCESS = "Listed all clients";
+
+
+ @Override
+ public CommandResult execute(Model model) {
+ assert model != null : "Model cannot be null";
+ requireNonNull(model);
+ model.updateFilteredClientList(PREDICATE_SHOW_ALL_CLIENTS);
+ return new CommandResult(MESSAGE_SUCCESS, TabIndex.Client);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/list/ListDeveloperCommand.java b/src/main/java/seedu/address/logic/commands/list/ListDeveloperCommand.java
new file mode 100644
index 00000000000..9ffa6d489dc
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/list/ListDeveloperCommand.java
@@ -0,0 +1,28 @@
+package seedu.address.logic.commands.list;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_DEVELOPERS;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.model.Model;
+
+/**
+ * Lists all persons in the address book to the user.
+ */
+public class ListDeveloperCommand extends Command {
+
+ public static final String COMMAND_WORD = "list-developer";
+
+ public static final String MESSAGE_SUCCESS = "Listed all developers";
+
+
+ @Override
+ public CommandResult execute(Model model) {
+ assert model != null : "Model cannot be null";
+ requireNonNull(model);
+ model.updateFilteredDeveloperList(PREDICATE_SHOW_ALL_DEVELOPERS);
+ return new CommandResult(MESSAGE_SUCCESS, TabIndex.Developer);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/list/ListProjectCommand.java b/src/main/java/seedu/address/logic/commands/list/ListProjectCommand.java
new file mode 100644
index 00000000000..8b8346371b4
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/list/ListProjectCommand.java
@@ -0,0 +1,29 @@
+package seedu.address.logic.commands.list;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PROJECTS;
+
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.model.Model;
+
+/**
+ * Lists all persons in the address book to the user.
+ */
+public class ListProjectCommand extends Command {
+
+ public static final String COMMAND_WORD = "list-project";
+
+ public static final String MESSAGE_SUCCESS = "Listed all projects";
+
+
+ @Override
+ public CommandResult execute(Model model) {
+ assert model != null : "Model cannot be null";
+ requireNonNull(model);
+ model.updateFilteredProjectList(PREDICATE_SHOW_ALL_PROJECTS);
+ model.updateFilteredProjectDeadlineList(unused -> true);
+ return new CommandResult(MESSAGE_SUCCESS, TabIndex.Project);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/mark/MarkDeadlineCommand.java b/src/main/java/seedu/address/logic/commands/mark/MarkDeadlineCommand.java
new file mode 100644
index 00000000000..0de839c9f5d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/mark/MarkDeadlineCommand.java
@@ -0,0 +1,136 @@
+package seedu.address.logic.commands.mark;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.edit.EditProjectCommand;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.logic.parser.edit.EditProjectCommandParser;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Model;
+import seedu.address.model.project.Project;
+
+/**
+ * Represents a command to mark a specific deadline within a project as completed.
+ * This command allows users to mark deadlines as done within a specified project.
+ */
+public class MarkDeadlineCommand extends Command {
+ public static final String COMMAND_WORD = "mark-deadline";
+ public static final String MESSAGE_SUCCESS = "The deadline has been marked as completed!";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Marks the specified deadline of the specified project "
+ + "as done. \n"
+ + "DEADLINE_INDEX must be a positive integer representing the index of the deadline in the displayed "
+ + "deadline table, and PROJECT_INDEX must be a positive integer which is the project's index number in the "
+ + "displayed project list. \n"
+ + "Parameters: "
+ + "PROJECT_INDEX DEADLINE_INDEX \n"
+ + "Example: " + COMMAND_WORD + " 1 2";
+
+ private final Index deadlineIndex;
+ private final Index projIndex;
+
+ /**
+ * Creates a MarkDeadlineCommand to mark a specific deadline as completed within a project.
+ *
+ * @param projIndex The index of the project containing the deadline to be marked.
+ * @param deadlineIndex The index of the deadline to be marked.
+ */
+ public MarkDeadlineCommand(Index projIndex, Index deadlineIndex) {
+ this.deadlineIndex = deadlineIndex;
+ this.projIndex = projIndex;
+ }
+
+ /**
+ * Executes the mark deadline operation by marking the specified deadline as completed within the project.
+ *
+ * @param model The model in which to mark the deadline as completed.
+ * @return A CommandResult indicating the success of the mark deadline operation.
+ * @throws CommandException If an error occurs during the execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredProjectList();
+ if (projIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PROJECT_DISPLAYED_INDEX);
+ }
+
+ Project projectToEdit = lastShownList.get(projIndex.getZeroBased());
+
+ if (deadlineIndex.getZeroBased() >= projectToEdit.deadlineListSize()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_DEADLINE_DISPLAYED_INDEX);
+ }
+
+ EditProjectCommand edit;
+
+ try {
+ edit = new EditProjectCommandParser().parse(editProjectArgs(
+ projectToEdit.markDeadlineStringRep(deadlineIndex.getZeroBased()),
+ projIndex.getOneBased()));
+ } catch (ParseException pe) {
+ throw new CommandException(pe.getMessage());
+ }
+
+ edit.execute(model);
+ return new CommandResult(MESSAGE_SUCCESS, TabIndex.Project);
+ }
+
+ /**
+ * Formats each element in a list of String representations into a String that will be used as the arguments
+ * parsed by an EditProjectCommandParser.
+ *
+ * @param stringRep The list containing the string representations of the Deadlines to be passed into the parser.
+ * @param index The index of the Project to edit.
+ * @return A String containing the index of the projects and the deadlines including the marked deadline.
+ */
+ private String editProjectArgs(List stringRep, int index) {
+ String res = "" + index;
+ for (String s : stringRep) {
+ res += " " + PREFIX_DEADLINE + s;
+ }
+ return res;
+ }
+
+ /**
+ * Checks if this MarkDeadlineCommand is equal to another object.
+ *
+ * @param other The object to compare with this MarkDeadlineCommand.
+ * @return True if the objects are equal, false otherwise.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof MarkDeadlineCommand)) {
+ return false;
+ }
+
+ MarkDeadlineCommand otherMarkDeadlineCommand = (MarkDeadlineCommand) other;
+ return deadlineIndex == otherMarkDeadlineCommand.deadlineIndex
+ && projIndex.equals(otherMarkDeadlineCommand.projIndex);
+ }
+
+ /**
+ * Generates a string representation of this MarkDeadlineCommand.
+ *
+ * @return A string representation of this object.
+ */
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("deadlineIndex", deadlineIndex)
+ .add("projIndex", projIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/mark/UnmarkDeadlineCommand.java b/src/main/java/seedu/address/logic/commands/mark/UnmarkDeadlineCommand.java
new file mode 100644
index 00000000000..c384e689625
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/mark/UnmarkDeadlineCommand.java
@@ -0,0 +1,124 @@
+package seedu.address.logic.commands.mark;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.edit.EditProjectCommand;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.logic.parser.edit.EditProjectCommandParser;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Model;
+import seedu.address.model.project.Project;
+
+/**
+ * Marks the specified deadline of the specified project as undone.
+ */
+public class UnmarkDeadlineCommand extends Command {
+ public static final String COMMAND_WORD = "unmark-deadline";
+ public static final String MESSAGE_SUCCESS = "The deadline has been marked as undone!";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Marks the specified deadline of the specified project "
+ + "as undone. \n"
+ + "DEADLINE_INDEX must be a positive integer representing the index of the deadline in the displayed "
+ + "deadline table, and PROJECT_INDEX must be a positive integer which is the project's index number in the "
+ + "displayed project list. \n"
+ + "Parameters: "
+ + "PROJECT_INDEX DEADLINE_INDEX \n"
+ + "Example: " + COMMAND_WORD + " 1 2";
+
+ private final Index deadlineIndex;
+ private final Index projIndex;
+
+ /**
+ * Creates an UnmarkDeadlineCommand to unmark the specified deadline of the specified project.
+ *
+ * @param projIndex The index of the project containing the deadline to be unmarked.
+ * @param deadlineIndex The index of the deadline to be unmarked within the project.
+ */
+ public UnmarkDeadlineCommand(Index projIndex, Index deadlineIndex) {
+ this.deadlineIndex = deadlineIndex;
+ this.projIndex = projIndex;
+ }
+ /**
+ * Executes the operation to unmark a project deadline as undone.
+ *
+ * @param model The model that contains the project data.
+ * @return A CommandResult indicating the success of the operation.
+ * @throws CommandException If an error occurs during the execution.
+ */
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredProjectList();
+ if (projIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PROJECT_DISPLAYED_INDEX);
+ }
+
+ Project projectToEdit = lastShownList.get(projIndex.getZeroBased());
+
+ if (deadlineIndex.getZeroBased() >= projectToEdit.deadlineListSize()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_DEADLINE_DISPLAYED_INDEX);
+ }
+
+ EditProjectCommand edit;
+
+ try {
+ edit = new EditProjectCommandParser().parse(editProjectArgs(
+ projectToEdit.unmarkDeadlineStringRep(deadlineIndex.getZeroBased()),
+ projIndex.getOneBased()));
+ } catch (ParseException pe) {
+ throw new CommandException(pe.getMessage());
+ }
+
+ edit.execute(model);
+ return new CommandResult(MESSAGE_SUCCESS, TabIndex.Project);
+ }
+
+ /**
+ * Formats each element in a list of String representations into a String that will be used as the arguments
+ * parsed by an EditProjectCommandParser.
+ *
+ * @param stringRep The list containing the string representations of the Deadlines to be passed into the parser.
+ * @param index The index of the Project to edit.
+ * @return A String containing the index of the projects and the deadlines including the unmarked deadline.
+ */
+ private String editProjectArgs(List stringRep, int index) {
+ String res = "" + index;
+ for (String s : stringRep) {
+ res += " " + PREFIX_DEADLINE + s;
+ }
+ return res;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof UnmarkDeadlineCommand)) {
+ return false;
+ }
+
+ UnmarkDeadlineCommand otherUnmarkDeadlineCommand = (UnmarkDeadlineCommand) other;
+ return deadlineIndex == otherUnmarkDeadlineCommand.deadlineIndex
+ && projIndex.equals(otherUnmarkDeadlineCommand.projIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("deadlineIndex", deadlineIndex)
+ .add("projIndex", projIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
deleted file mode 100644
index 4ff1a97ed77..00000000000
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import java.util.Set;
-import java.util.stream.Stream;
-
-import seedu.address.logic.commands.AddCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-
-/**
- * Parses input arguments and creates a new AddCommand object
- */
-public class AddCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the AddCommand
- * and returns an AddCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
- */
- public AddCommand parse(String args) throws ParseException {
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
-
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
- || !argMultimap.getPreamble().isEmpty()) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
- }
-
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
- Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
- Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get());
- Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get());
- Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get());
- Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
-
- Person person = new Person(name, phone, email, address, tagList);
-
- return new AddCommand(person);
- }
-
- /**
- * Returns true if none of the prefixes contains empty {@code Optional} values in the given
- * {@code ArgumentMultimap}.
- */
- private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
- return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 3149ee07e0b..a7561a06a8c 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -2,22 +2,69 @@
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
+import static seedu.address.logic.Messages.MESSAGE_VALID_LOCKED_COMMANDS;
+import static seedu.address.logic.Messages.MESSAGE_VALID_UNLOCKED_COMMANDS;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import seedu.address.commons.core.LogsCenter;
-import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.ChangePasswordCommand;
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.Command;
-import seedu.address.logic.commands.DeleteCommand;
-import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
-import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
-import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.LockCommand;
+import seedu.address.logic.commands.RedoCommand;
+import seedu.address.logic.commands.UndoCommand;
+import seedu.address.logic.commands.UnlockCommand;
+import seedu.address.logic.commands.add.AddClientCommand;
+import seedu.address.logic.commands.add.AddDeveloperCommand;
+import seedu.address.logic.commands.add.AddProjectCommand;
+import seedu.address.logic.commands.addroles.AddClientRoleCommand;
+import seedu.address.logic.commands.addroles.AddDeveloperRoleCommand;
+import seedu.address.logic.commands.delete.DeleteClientCommand;
+import seedu.address.logic.commands.delete.DeleteDeveloperCommand;
+import seedu.address.logic.commands.delete.DeleteProjectCommand;
+import seedu.address.logic.commands.deleteroles.DeleteClientRoleCommand;
+import seedu.address.logic.commands.deleteroles.DeleteDeveloperRoleCommand;
+import seedu.address.logic.commands.edit.EditClientCommand;
+import seedu.address.logic.commands.edit.EditDeveloperCommand;
+import seedu.address.logic.commands.edit.EditProjectCommand;
+import seedu.address.logic.commands.find.FindClientCommand;
+import seedu.address.logic.commands.find.FindDeadlineCommand;
+import seedu.address.logic.commands.find.FindDeveloperCommand;
+import seedu.address.logic.commands.find.FindProjectCommand;
+import seedu.address.logic.commands.imports.ImportClientCommand;
+import seedu.address.logic.commands.imports.ImportDeveloperCommand;
+import seedu.address.logic.commands.list.ListClientCommand;
+import seedu.address.logic.commands.list.ListDeveloperCommand;
+import seedu.address.logic.commands.list.ListProjectCommand;
+import seedu.address.logic.commands.mark.MarkDeadlineCommand;
+import seedu.address.logic.commands.mark.UnmarkDeadlineCommand;
+import seedu.address.logic.parser.add.AddClientCommandParser;
+import seedu.address.logic.parser.add.AddDeveloperCommandParser;
+import seedu.address.logic.parser.add.AddProjectCommandParser;
+import seedu.address.logic.parser.addroles.AddClientRoleCommandParser;
+import seedu.address.logic.parser.addroles.AddDeveloperRoleCommandParser;
+import seedu.address.logic.parser.delete.DeleteClientCommandParser;
+import seedu.address.logic.parser.delete.DeleteDeveloperCommandParser;
+import seedu.address.logic.parser.delete.DeleteProjectCommandParser;
+import seedu.address.logic.parser.deleteroles.DeleteClientRoleCommandParser;
+import seedu.address.logic.parser.deleteroles.DeleteDeveloperRoleCommandParser;
+import seedu.address.logic.parser.edit.EditClientCommandParser;
+import seedu.address.logic.parser.edit.EditDeveloperCommandParser;
+import seedu.address.logic.parser.edit.EditProjectCommandParser;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.logic.parser.find.FindClientCommandParser;
+import seedu.address.logic.parser.find.FindDeadlineCommandParser;
+import seedu.address.logic.parser.find.FindDeveloperCommandParser;
+import seedu.address.logic.parser.find.FindProjectCommandParser;
+import seedu.address.logic.parser.imports.ImportClientCommandParser;
+import seedu.address.logic.parser.imports.ImportDeveloperCommandParser;
+import seedu.address.logic.parser.mark.MarkDeadlineCommandParser;
+import seedu.address.logic.parser.mark.UnmarkDeadlineCommandParser;
/**
* Parses user input.
@@ -29,6 +76,15 @@ public class AddressBookParser {
*/
private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)");
private static final Logger logger = LogsCenter.getLogger(AddressBookParser.class);
+ private static boolean isLocked = true;
+
+ public static void lock() {
+ isLocked = true;
+ }
+
+ public static void unlock() {
+ isLocked = false;
+ }
/**
* Parses user input into command for execution.
@@ -50,37 +106,118 @@ public Command parseCommand(String userInput) throws ParseException {
// log messages such as the one below.
// Lower level log messages are used sparingly to minimize noise in the code.
logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);
+ if (isLocked == false) {
+ switch (commandWord) {
+ case AddDeveloperCommand.COMMAND_WORD:
+ return new AddDeveloperCommandParser().parse(arguments);
+
+ case AddClientCommand.COMMAND_WORD:
+ return new AddClientCommandParser().parse(arguments);
+
+ case AddProjectCommand.COMMAND_WORD:
+ return new AddProjectCommandParser().parse(arguments);
+
+ case AddDeveloperRoleCommand.COMMAND_WORD:
+ return new AddDeveloperRoleCommandParser().parse(arguments);
+
+ case AddClientRoleCommand.COMMAND_WORD:
+ return new AddClientRoleCommandParser().parse(arguments);
+
+ case ImportDeveloperCommand.COMMAND_WORD:
+ return new ImportDeveloperCommandParser().parse(arguments);
+
+ case ImportClientCommand.COMMAND_WORD:
+ return new ImportClientCommandParser().parse(arguments);
+ case FindDeadlineCommand.COMMAND_WORD:
+ return new FindDeadlineCommandParser().parse(arguments);
+
+ case EditDeveloperCommand.COMMAND_WORD:
+ return new EditDeveloperCommandParser().parse(arguments);
+
+ case EditClientCommand.COMMAND_WORD:
+ return new EditClientCommandParser().parse(arguments);
+
+ case EditProjectCommand.COMMAND_WORD:
+ return new EditProjectCommandParser().parse(arguments);
+
+ case DeleteDeveloperCommand.COMMAND_WORD:
+ return new DeleteDeveloperCommandParser().parse(arguments);
- switch (commandWord) {
+ case DeleteClientCommand.COMMAND_WORD:
+ return new DeleteClientCommandParser().parse(arguments);
- case AddCommand.COMMAND_WORD:
- return new AddCommandParser().parse(arguments);
+ case DeleteProjectCommand.COMMAND_WORD:
+ return new DeleteProjectCommandParser().parse(arguments);
- case EditCommand.COMMAND_WORD:
- return new EditCommandParser().parse(arguments);
+ case DeleteDeveloperRoleCommand.COMMAND_WORD:
+ return new DeleteDeveloperRoleCommandParser().parse(arguments);
- case DeleteCommand.COMMAND_WORD:
- return new DeleteCommandParser().parse(arguments);
+ case DeleteClientRoleCommand.COMMAND_WORD:
+ return new DeleteClientRoleCommandParser().parse(arguments);
- case ClearCommand.COMMAND_WORD:
- return new ClearCommand();
+ case ClearCommand.COMMAND_WORD:
+ return new ClearCommand();
- case FindCommand.COMMAND_WORD:
- return new FindCommandParser().parse(arguments);
+ case FindDeveloperCommand.COMMAND_WORD:
+ return new FindDeveloperCommandParser().parse(arguments);
- case ListCommand.COMMAND_WORD:
- return new ListCommand();
+ case FindClientCommand.COMMAND_WORD:
+ return new FindClientCommandParser().parse(arguments);
- case ExitCommand.COMMAND_WORD:
- return new ExitCommand();
+ case FindProjectCommand.COMMAND_WORD:
+ return new FindProjectCommandParser().parse(arguments);
- case HelpCommand.COMMAND_WORD:
- return new HelpCommand();
+ case ListClientCommand.COMMAND_WORD:
+ return new ListClientCommand();
- default:
- logger.finer("This user input caused a ParseException: " + userInput);
- throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
+ case ListDeveloperCommand.COMMAND_WORD:
+ return new ListDeveloperCommand();
+
+ case ListProjectCommand.COMMAND_WORD:
+ return new ListProjectCommand();
+
+ case UndoCommand.COMMAND_WORD:
+ return new UndoCommand();
+
+ case RedoCommand.COMMAND_WORD:
+ return new RedoCommand();
+
+ case ExitCommand.COMMAND_WORD:
+ return new ExitCommand();
+
+ case HelpCommand.COMMAND_WORD:
+ return new HelpCommand();
+
+ case MarkDeadlineCommand.COMMAND_WORD:
+ return new MarkDeadlineCommandParser().parse(arguments);
+
+ case UnmarkDeadlineCommand.COMMAND_WORD:
+ return new UnmarkDeadlineCommandParser().parse(arguments);
+
+ case LockCommand.COMMAND_WORD:
+ return new LockCommand();
+
+ case ChangePasswordCommand.COMMAND_WORD:
+ return new ChangePasswordCommandParser().parse(arguments);
+
+ default:
+ logger.finer("This user input caused a ParseException: " + userInput);
+ throw new ParseException(MESSAGE_UNKNOWN_COMMAND + "\n" + MESSAGE_VALID_UNLOCKED_COMMANDS);
+ }
+ } else {
+ switch (commandWord) {
+ case UnlockCommand.COMMAND_WORD:
+ return new UnlockCommandParser().parse(arguments);
+ case ExitCommand.COMMAND_WORD:
+ return new ExitCommand();
+
+ case HelpCommand.COMMAND_WORD:
+ return new HelpCommand();
+
+ default:
+ logger.finer("This user input caused a ParseException: " + userInput);
+ throw new ParseException(MESSAGE_UNKNOWN_COMMAND + "\n" + MESSAGE_VALID_LOCKED_COMMANDS);
+ }
}
}
-
}
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
index 21e26887a83..3bb07af2dee 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
+++ b/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java
@@ -19,7 +19,9 @@
*/
public class ArgumentMultimap {
- /** Prefixes mapped to their respective arguments**/
+ /**
+ * Prefixes mapped to their respective arguments
+ **/
private final Map> argMultimap = new HashMap<>();
/**
@@ -75,4 +77,11 @@ public void verifyNoDuplicatePrefixesFor(Prefix... prefixes) throws ParseExcepti
throw new ParseException(Messages.getErrorMessageForDuplicatePrefixes(duplicatedPrefixes));
}
}
+
+ /**
+ * Returns a boolean representing whether there are any prefix mappings in the ArgumentMultimap.
+ */
+ public boolean hasMappings() {
+ return !argMultimap.isEmpty();
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
index 5c9aebfa488..3be8e89059d 100644
--- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
+++ b/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java
@@ -7,11 +7,11 @@
/**
* Tokenizes arguments string of the form: {@code preamble value value ...}
- * e.g. {@code some preamble text t/ 11.00 t/12.00 k/ m/ July} where prefixes are {@code t/ k/ m/}.
+ * e.g. {@code some preamble text t/ 11.00 t/12.00 k/ m/ July} where prefixes are {@code t/ k/ m/}.
* 1. An argument's value can be an empty string e.g. the value of {@code k/} in the above example.
* 2. Leading and trailing whitespaces of an argument value will be discarded.
* 3. An argument may be repeated and all its values will be accumulated e.g. the value of {@code t/}
- * in the above example.
+ * in the above example.
*/
public class ArgumentTokenizer {
@@ -21,7 +21,7 @@ public class ArgumentTokenizer {
*
* @param argsString Arguments string of the form: {@code preamble value value ...}
* @param prefixes Prefixes to tokenize the arguments string with
- * @return ArgumentMultimap object that maps prefixes to their arguments
+ * @return ArgumentMultimap object that maps prefixes to their arguments
*/
public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) {
List positions = findAllPrefixPositions(argsString, prefixes);
@@ -33,7 +33,7 @@ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) {
*
* @param argsString Arguments string of the form: {@code preamble value value ...}
* @param prefixes Prefixes to find in the arguments string
- * @return List of zero-based prefix positions in the given arguments string
+ * @return List of zero-based prefix positions in the given arguments string
*/
private static List findAllPrefixPositions(String argsString, Prefix... prefixes) {
return Arrays.stream(prefixes)
@@ -62,7 +62,7 @@ private static List findPrefixPositions(String argsString, Prefi
* {@code argsString} starting from index {@code fromIndex}. An occurrence
* is valid if there is a whitespace before {@code prefix}. Returns -1 if no
* such occurrence can be found.
- *
+ *
* E.g if {@code argsString} = "e/hip/900", {@code prefix} = "p/" and
* {@code fromIndex} = 0, this method returns -1 as there are no valid
* occurrences of "p/" with whitespace before it. However, if
@@ -82,7 +82,7 @@ private static int findPrefixPosition(String argsString, String prefix, int from
*
* @param argsString Arguments string of the form: {@code preamble value value ...}
* @param prefixPositions Zero-based positions of all prefixes in {@code argsString}
- * @return ArgumentMultimap object that maps prefixes to their arguments
+ * @return ArgumentMultimap object that maps prefixes to their arguments
*/
private static ArgumentMultimap extractArguments(String argsString, List prefixPositions) {
@@ -114,8 +114,8 @@ private static ArgumentMultimap extractArguments(String argsString, List {
+ /**
+ * Parses user input and creates a {@link ChangePasswordCommand} based on the provided passwords.
+ *
+ * @param args User input arguments.
+ * @return A {@link ChangePasswordCommand} for changing the password.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ @Override
+ public ChangePasswordCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PASSWORD, PREFIX_NEW_PASSWORD);
+ if (argMultimap.getValue(PREFIX_PASSWORD).isPresent()
+ && argMultimap.getValue(PREFIX_NEW_PASSWORD).isPresent()) {
+ String currentPw = ParserUtil.parsePassword(argMultimap.getValue(PREFIX_PASSWORD).get());
+ String newPw = ParserUtil.parsePassword(argMultimap.getValue(PREFIX_NEW_PASSWORD).get());
+ return new ChangePasswordCommand(currentPw, newPw);
+ }
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, ChangePasswordCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..fb3977ae230 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -10,6 +10,19 @@ public class CliSyntax {
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 Prefix PREFIX_PROJECT = new Prefix("pr/");
+ public static final Prefix PREFIX_DATEJOINED = new Prefix("d/");
+ public static final Prefix PREFIX_ROLE = new Prefix("r/");
+ public static final Prefix PREFIX_SALARY = new Prefix("s/");
+ public static final Prefix PREFIX_GITHUBID = new Prefix("g/");
+ public static final Prefix PREFIX_RATING = new Prefix("rt/");
+ public static final Prefix PREFIX_ORGANISATION = new Prefix("o/");
+ public static final Prefix PREFIX_DOCUMENT = new Prefix("do/");
+ public static final Prefix PREFIX_DESCRIPTION = new Prefix("dr/");
+ public static final Prefix PREFIX_DEADLINE = new Prefix("dl/");
+ public static final Prefix PREFIX_PASSWORD = new Prefix("pw/");
+ public static final Prefix PREFIX_NEW_PASSWORD = new Prefix("npw/");
+ public static final Prefix PREFIX_PRIORITY = new Prefix("pri/");
+
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
deleted file mode 100644
index 3527fe76a3e..00000000000
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.DeleteCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
-
-/**
- * Parses input arguments and creates a new DeleteCommand object
- */
-public class DeleteCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the DeleteCommand
- * and returns a DeleteCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
- */
- public DeleteCommand parse(String args) throws ParseException {
- try {
- Index index = ParserUtil.parseIndex(args);
- return new DeleteCommand(index);
- } 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
deleted file mode 100644
index 46b3309a78b..00000000000
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package seedu.address.logic.parser;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Optional;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.tag.Tag;
-
-/**
- * Parses input arguments and creates a new EditCommand object
- */
-public class EditCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the EditCommand
- * and returns an EditCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
- */
- public EditCommand parse(String args) throws ParseException {
- requireNonNull(args);
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
-
- Index index;
-
- try {
- index = ParserUtil.parseIndex(argMultimap.getPreamble());
- } catch (ParseException pe) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
- }
-
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
-
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
-
- if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
- editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
- }
- if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
- editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
- }
- if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
- editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
- }
- if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
- editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
- }
- parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
-
- if (!editPersonDescriptor.isAnyFieldEdited()) {
- throw new ParseException(EditCommand.MESSAGE_NOT_EDITED);
- }
-
- return new EditCommand(index, editPersonDescriptor);
- }
-
- /**
- * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty.
- * If {@code tags} contain only one element which is an empty string, it will be parsed into a
- * {@code Set} containing zero tags.
- */
- private Optional> parseTagsForEdit(Collection tags) throws ParseException {
- assert tags != null;
-
- if (tags.isEmpty()) {
- return Optional.empty();
- }
- Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags;
- return Optional.of(ParserUtil.parseTags(tagSet));
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java
deleted file mode 100644
index 2867bde857b..00000000000
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-
-import java.util.Arrays;
-
-import seedu.address.logic.commands.FindCommand;
-import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-
-/**
- * Parses input arguments and creates a new FindCommand object
- */
-public class FindCommandParser implements Parser {
-
- /**
- * Parses the given {@code String} of arguments in the context of the FindCommand
- * and returns a FindCommand object for execution.
- * @throws ParseException if the user input does not conform the expected format
- */
- public FindCommand parse(String args) throws ParseException {
- String trimmedArgs = args.trim();
- if (trimmedArgs.isEmpty()) {
- throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
- }
-
- String[] nameKeywords = trimmedArgs.split("\\s+");
-
- return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
- }
-
-}
diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/address/logic/parser/Parser.java
index d6551ad8e3f..ce644a9c6fd 100644
--- a/src/main/java/seedu/address/logic/parser/Parser.java
+++ b/src/main/java/seedu/address/logic/parser/Parser.java
@@ -10,6 +10,7 @@ public interface Parser {
/**
* Parses {@code userInput} into a command and returns it.
+ *
* @throws ParseException if {@code userInput} does not conform the expected format
*/
T parse(String userInput) throws ParseException;
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..723ef907d9b 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -2,18 +2,32 @@
import static java.util.Objects.requireNonNull;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.StringUtil;
+import seedu.address.logic.Messages;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.Password;
+import seedu.address.model.client.ClientRoles;
+import seedu.address.model.client.Document;
+import seedu.address.model.commons.Date;
+import seedu.address.model.commons.Name;
+import seedu.address.model.developer.DeveloperRoles;
+import seedu.address.model.developer.GithubId;
+import seedu.address.model.developer.Rating;
+import seedu.address.model.developer.Salary;
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.model.project.Deadline;
+import seedu.address.model.project.Description;
+import seedu.address.model.project.Priority;
+import seedu.address.model.project.Project;
/**
* Contains utility methods used for parsing strings in the various *Parser classes.
@@ -25,6 +39,7 @@ public class ParserUtil {
/**
* Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
* trimmed.
+ *
* @throws ParseException if the specified index is invalid (not non-zero unsigned integer).
*/
public static Index parseIndex(String oneBasedIndex) throws ParseException {
@@ -96,29 +111,237 @@ public static Email parseEmail(String email) throws ParseException {
}
/**
- * Parses a {@code String tag} into a {@code Tag}.
+ * Parses a {@code String project} into a {@code Project}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code Project} is invalid.
+ */
+ public static Project parseProject(String project) throws ParseException {
+ requireNonNull(project);
+ String trimmedProject = project.trim();
+ if (!Name.isValidName(trimmedProject)) {
+ throw new ParseException(Name.MESSAGE_CONSTRAINTS);
+ }
+ return new Project(new Name(trimmedProject), new Description(""), new ArrayList());
+ }
+
+ /**
+ * Parses {@code Collection projects} into a {@code Set}.
+ */
+ public static Set parseProjects(Collection projects) throws ParseException {
+ requireNonNull(projects);
+ final Set projectSet = new HashSet<>();
+ for (String projectName : projects) {
+ projectSet.add(parseProject(projectName));
+ }
+ return projectSet;
+ }
+
+ /**
+ * Parses {@code Collection projects} into a {@code Set}.
+ *
+ * @param projects The Collection of projects to parse.
+ * @returns A HashSet of String.
+ */
+ public static Set parseProjectsToSet(Collection projects) {
+ requireNonNull(projects);
+ final Set projectSet = new HashSet<>();
+
+ for (String p : projects) {
+ projectSet.add(p);
+ }
+ return projectSet;
+ }
+
+ /**
+ * Parses {@code Collection deadlines} into a {@code List}.
+ *
+ * @param deadlines The Collection of deadlines to parse.
+ * @throws ParseException if format is invalid.
+ * @returns An ArrayList of Deadlines if parsing is successful.
+ */
+ public static List parseDeadlines(Collection deadlines) throws ParseException {
+ requireNonNull(deadlines);
+ final List deadlineSet = new ArrayList<>();
+ for (String str : deadlines) {
+ if (!Deadline.isValidDeadline(str)) {
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ Deadline.MESSAGE_CONSTRAINTS));
+ } else {
+ deadlineSet.add(new Deadline(str, deadlineSet.size() + 1));
+ }
+ }
+ return deadlineSet;
+ }
+
+ /**
+ * Parses a {@code String dateJoined} into a {@code DateJoined}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code DateJoined} is invalid.
+ */
+ public static Date parseDateJoined(String dateJoined) throws ParseException {
+ requireNonNull(dateJoined);
+ String trimmedDateJoined = dateJoined.trim();
+ if (!Date.isValidDate(trimmedDateJoined, false)) {
+ throw new ParseException(Date.MESSAGE_CONSTRAINTS);
+ }
+ return new Date(trimmedDateJoined, false);
+ }
+
+ /**
+ * Parses a {@code String dateDeadline} into a {@code DateDeadline}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code DateDeadline} is invalid.
+ */
+ public static Date parseDateDeadline(String dateJoined) throws ParseException {
+ requireNonNull(dateJoined);
+ String trimmedDateJoined = dateJoined.trim();
+ if (!Date.isValidDate(trimmedDateJoined, true)) {
+ throw new ParseException(Date.MESSAGE_CONSTRAINTS);
+ }
+ return new Date(trimmedDateJoined, true);
+ }
+
+
+ /**
+ * Parses a {@code String role} into a {@code Role}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code Role} is invalid.
+ */
+ public static DeveloperRoles parseDeveloperRole(String role) throws ParseException {
+ requireNonNull(role);
+ String trimmedRole = role.trim();
+ if (!DeveloperRoles.isValidRole(trimmedRole)) {
+ throw new ParseException(DeveloperRoles.NO_SUCH_DEVELOPER_ROLE);
+ }
+ return new DeveloperRoles(trimmedRole);
+ }
+
+ /**
+ * Parses a client role string and returns a `ClientRoles` object.
+ *
+ * @param role The client role string to be parsed.
+ * @return A `ClientRoles` object representing the parsed client role.
+ * @throws ParseException If the input string is not a valid client role.
+ */
+ public static ClientRoles parseClientRole(String role) throws ParseException {
+ requireNonNull(role);
+ String trimmedRole = role.trim();
+ if (!ClientRoles.isValidRole(trimmedRole)) {
+ throw new ParseException(ClientRoles.NO_SUCH_CLIENT_ROLE);
+ }
+ return new ClientRoles(trimmedRole);
+ }
+
+ /**
+ * Parses a {@code String salary} into a {@code Salary}.
* Leading and trailing whitespaces will be trimmed.
*
- * @throws ParseException if the given {@code tag} is invalid.
+ * @throws ParseException if the given {@code Salary} 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);
+ public static Salary parseSalary(String salary) throws ParseException {
+ requireNonNull(salary);
+ String trimmedSalary = salary.trim();
+ if (!Salary.isValidSalary(trimmedSalary)) {
+ throw new ParseException(Salary.MESSAGE_CONSTRAINTS);
}
- return new Tag(trimmedTag);
+ return new Salary(trimmedSalary);
}
/**
- * Parses {@code Collection tags} into a {@code Set}.
+ * Parses a GitHub ID string and returns a `GithubId` object.
+ *
+ * @param githubid The GitHub ID string to be parsed.
+ * @return A `GithubId` object representing the parsed GitHub ID.
+ * @throws ParseException If the input string is not a valid GitHub ID.
+ */
+ public static GithubId parseGithubId(String githubid) throws ParseException {
+ requireNonNull(githubid);
+ String trimmedGithubId = githubid.trim();
+ if (!GithubId.isValidGithubId(trimmedGithubId)) {
+ throw new ParseException(GithubId.MESSAGE_CONSTRAINTS);
+ }
+ return new GithubId(trimmedGithubId);
+ }
+
+ /**
+ * Parses a rating string and returns a `Rating` object.
+ *
+ * @param rating The rating string to be parsed.
+ * @return A `Rating` object representing the parsed rating.
+ * @throws ParseException If the input string is not a valid rating.
+ */
+ public static Rating parseRating(String rating) throws ParseException {
+ requireNonNull(rating);
+ String trimmedRating = rating.trim();
+ if (!Rating.isValidRating(trimmedRating)) {
+ throw new ParseException(Rating.MESSAGE_CONSTRAINTS);
+ }
+ return new Rating(trimmedRating);
+ }
+
+ /**
+ * Parses a document URL string and returns a `Document` object.
+ *
+ * @param doc The document URL string to be parsed.
+ * @return A `Document` object representing the parsed document URL.
+ * @throws ParseException If the input string is not a valid document URL.
+ */
+ public static Document parseDocument(String doc) throws ParseException {
+ requireNonNull(doc);
+ String trimmedDoc = doc.trim();
+ if (!Document.isValidUrl(trimmedDoc)) {
+ throw new ParseException(Document.MESSAGE_CONSTRAINTS);
+ }
+ return new Document(trimmedDoc);
+ }
+
+ /**
+ * Parses a description string and returns a `Description` object.
+ *
+ * @param description The description string to be parsed.
+ * @return A `Description` object representing the parsed description.
+ * @throws ParseException If the input string is not a valid description.
+ */
+ public static Description parseDescription(String description) throws ParseException {
+ requireNonNull(description);
+ String trimmedDescription = description.trim();
+ if (!Description.isValidDescription(trimmedDescription)) {
+ throw new ParseException(Description.MESSAGE_CONSTRAINTS);
+ }
+ return new Description(trimmedDescription);
+ }
+
+ /**
+ * Parses a password string and returns the trimmed password.
+ *
+ * @param pw The password string to be parsed.
+ * @return The trimmed password.
+ * @throws ParseException If the input string is not a valid password.
+ */
+ public static String parsePassword(String pw) throws ParseException {
+ requireNonNull(pw);
+ String trimmedPw = pw.trim();
+ if (!Password.isValidPassword(trimmedPw)) {
+ throw new ParseException(Password.MESSAGE_CONSTRAINTS);
+ }
+ return trimmedPw;
+ }
+
+ /**
+ * Parses a priority string and returns the corresponding Priority enum.
+ *
+ * @param priorityKeywords The priority string to be parsed.
+ * @return The corresponding Priority enum.
+ * @throws ParseException If the input string is not a valid priority.
*/
- public static Set parseTags(Collection tags) throws ParseException {
- requireNonNull(tags);
- final Set tagSet = new HashSet<>();
- for (String tagName : tags) {
- tagSet.add(parseTag(tagName));
+ public static Priority parsePriority(String priorityKeywords) throws ParseException {
+ if (!(priorityKeywords.equals("HIGH") || priorityKeywords.equals("MEDIUM") || priorityKeywords.equals("LOW"))) {
+ throw new ParseException("Priority has to be HIGH, MEDIUM or LOW");
}
- return tagSet;
+ return Priority.valueOf(priorityKeywords);
}
}
diff --git a/src/main/java/seedu/address/logic/parser/UnlockCommandParser.java b/src/main/java/seedu/address/logic/parser/UnlockCommandParser.java
new file mode 100644
index 00000000000..bcc990b0efa
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/UnlockCommandParser.java
@@ -0,0 +1,33 @@
+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_PASSWORD;
+
+import seedu.address.logic.commands.UnlockCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses the user input to create an UnlockCommand.
+ */
+public class UnlockCommandParser implements Parser {
+ /**
+ * Parses the given {@code args} and returns an UnlockCommand.
+ *
+ * @param args User input arguments.
+ * @return An UnlockCommand for unlocking the application.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ @Override
+ public UnlockCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_PASSWORD);
+
+ if (argMultimap.getValue(PREFIX_PASSWORD).isPresent()) {
+ String input = ParserUtil.parsePassword(argMultimap.getValue(PREFIX_PASSWORD).get());
+ return new UnlockCommand(input);
+ }
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnlockCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/add/AddClientCommandParser.java b/src/main/java/seedu/address/logic/parser/add/AddClientCommandParser.java
new file mode 100644
index 00000000000..0c1166cbf3c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/add/AddClientCommandParser.java
@@ -0,0 +1,94 @@
+package seedu.address.logic.parser.add;
+
+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_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOCUMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GITHUBID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ORGANISATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.add.AddClientCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.client.Client;
+import seedu.address.model.client.ClientRoles;
+import seedu.address.model.client.Document;
+import seedu.address.model.commons.Name;
+import seedu.address.model.person.Address;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Phone;
+
+/**
+ * Parses input arguments and creates a new AddClientCommand object
+ */
+public class AddClientCommandParser implements Parser {
+ //Name name, Phone phone, Email email, Address address, Role role, Set projects,
+ // Name organisation, Document document
+
+ /**
+ * 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());
+ }
+
+ /**
+ * Parses user input into an AddClientCommand.
+ *
+ * @param args User input arguments.
+ * @return An AddClientCommand to add a new client.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ public AddClientCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ADDRESS, PREFIX_PROJECT, PREFIX_DATEJOINED, PREFIX_ROLE, PREFIX_SALARY, PREFIX_GITHUBID,
+ PREFIX_RATING, PREFIX_ORGANISATION, PREFIX_DOCUMENT, PREFIX_DESCRIPTION, PREFIX_DEADLINE);
+
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ROLE, PREFIX_ORGANISATION, PREFIX_DOCUMENT)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddClientCommand.MESSAGE_USAGE));
+ }
+
+ for (Prefix p : Client.UNUSED_PREFIXES) {
+ if (argMultimap.getValue(p).isPresent()) {
+ throw new ParseException(String.format(Messages.MESSAGE_INAPPLICABLE_PREFIX_USED,
+ AddClientCommand.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());
+ ClientRoles role = ParserUtil.parseClientRole(argMultimap.getValue(PREFIX_ROLE).get());
+ Set projectList = new HashSet<>(argMultimap.getAllValues(PREFIX_PROJECT));
+ Name organisation = ParserUtil.parseName(argMultimap.getValue(PREFIX_ORGANISATION).get());
+ Document document = ParserUtil.parseDocument(argMultimap.getValue(PREFIX_DOCUMENT).get());
+
+ Client client = new Client(name, phone, email, address, role, projectList, organisation, document);
+
+ return new AddClientCommand(client);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/add/AddDeveloperCommandParser.java b/src/main/java/seedu/address/logic/parser/add/AddDeveloperCommandParser.java
new file mode 100644
index 00000000000..9cbb7c3bb1f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/add/AddDeveloperCommandParser.java
@@ -0,0 +1,98 @@
+package seedu.address.logic.parser.add;
+
+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_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOCUMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GITHUBID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ORGANISATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import java.text.SimpleDateFormat;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.add.AddDeveloperCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.commons.Date;
+import seedu.address.model.commons.Name;
+import seedu.address.model.developer.Developer;
+import seedu.address.model.developer.DeveloperRoles;
+import seedu.address.model.developer.GithubId;
+import seedu.address.model.developer.Rating;
+import seedu.address.model.developer.Salary;
+import seedu.address.model.person.Address;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Phone;
+
+/**
+ * Parses input arguments and creates a new AddDeveloperCommand object
+ */
+public class AddDeveloperCommandParser implements Parser {
+
+ /**
+ * 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());
+ }
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddDeveloperCommand
+ * and returns an AddDeveloperCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddDeveloperCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ADDRESS, PREFIX_PROJECT, PREFIX_DATEJOINED, PREFIX_ROLE, PREFIX_SALARY, PREFIX_GITHUBID,
+ PREFIX_RATING, PREFIX_ORGANISATION, PREFIX_DOCUMENT, PREFIX_DESCRIPTION, PREFIX_DEADLINE);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_SALARY, PREFIX_ROLE)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddDeveloperCommand.MESSAGE_USAGE));
+ }
+
+ for (Prefix p : Developer.UNUSED_PREFIXES) {
+ if (argMultimap.getValue(p).isPresent()) {
+ throw new ParseException(String.format(Messages.MESSAGE_INAPPLICABLE_PREFIX_USED,
+ AddDeveloperCommand.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());
+ Date dateJoined = ParserUtil.parseDateJoined(argMultimap.getValue(PREFIX_DATEJOINED)
+ .orElse(new SimpleDateFormat("dd-MM-yyyy").format(new java.util.Date())));
+ DeveloperRoles role = ParserUtil.parseDeveloperRole(argMultimap.getValue(PREFIX_ROLE).get());
+ Salary salary = ParserUtil.parseSalary(argMultimap.getValue(PREFIX_SALARY).get());
+ Set projectList = ParserUtil.parseProjectsToSet(argMultimap.getAllValues(PREFIX_PROJECT));
+ GithubId githubId = ParserUtil.parseGithubId(argMultimap.getValue(PREFIX_GITHUBID).orElse(""));
+ Rating rating = ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).orElse("0"));
+
+ Developer developer = new Developer(name, phone, email, address, role, projectList, salary,
+ dateJoined, githubId, rating);
+
+ return new AddDeveloperCommand(developer);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/add/AddProjectCommandParser.java b/src/main/java/seedu/address/logic/parser/add/AddProjectCommandParser.java
new file mode 100644
index 00000000000..68f4e817ac9
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/add/AddProjectCommandParser.java
@@ -0,0 +1,81 @@
+package seedu.address.logic.parser.add;
+
+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_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOCUMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GITHUBID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ORGANISATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.add.AddProjectCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.commons.Name;
+import seedu.address.model.project.Deadline;
+import seedu.address.model.project.Description;
+import seedu.address.model.project.Project;
+
+/**
+ * Parses input arguments and creates a new AddProjectCommand object
+ */
+public class AddProjectCommandParser implements Parser {
+
+ /**
+ * 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());
+ }
+
+ /**
+ * Parses user input into an AddProjectCommand.
+ *
+ * @param args User input arguments.
+ * @return An AddProjectCommand to add a new project.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ public AddProjectCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ADDRESS, PREFIX_PROJECT, PREFIX_DATEJOINED, PREFIX_ROLE, PREFIX_SALARY, PREFIX_GITHUBID,
+ PREFIX_RATING, PREFIX_ORGANISATION, PREFIX_DOCUMENT, PREFIX_DESCRIPTION, PREFIX_DEADLINE);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_DESCRIPTION)
+ || !argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddProjectCommand.MESSAGE_USAGE));
+ }
+
+ for (Prefix p : Project.UNUSED_PREFIXES) {
+ if (argMultimap.getValue(p).isPresent()) {
+ throw new ParseException(String.format(Messages.MESSAGE_INAPPLICABLE_PREFIX_USED,
+ AddProjectCommand.MESSAGE_USAGE));
+ }
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_DESCRIPTION);
+ Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get());
+ Description desc = ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get());
+ List deadlineList = argMultimap.getAllValues(PREFIX_DEADLINE);
+ List deadlines = ParserUtil.parseDeadlines(deadlineList);
+
+ Project project = new Project(name, desc, deadlines);
+ return new AddProjectCommand(project);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/addroles/AddClientRoleCommandParser.java b/src/main/java/seedu/address/logic/parser/addroles/AddClientRoleCommandParser.java
new file mode 100644
index 00000000000..fa736202d78
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/addroles/AddClientRoleCommandParser.java
@@ -0,0 +1,30 @@
+package seedu.address.logic.parser.addroles;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.addroles.AddClientRoleCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses user input to create an AddClientRoleCommand.
+ */
+public class AddClientRoleCommandParser implements Parser {
+ /**
+ * Parses the given {@code args} and returns an AddClientRoleCommand.
+ *
+ * @param args User input arguments.
+ * @return An AddClientRoleCommand for adding a client role.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ @Override
+ public AddClientRoleCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ if (!args.isBlank()) {
+ String role = args.substring(1).trim();
+ return new AddClientRoleCommand(role);
+ } else {
+ throw new ParseException("Role cannot be empty!");
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/addroles/AddDeveloperRoleCommandParser.java b/src/main/java/seedu/address/logic/parser/addroles/AddDeveloperRoleCommandParser.java
new file mode 100644
index 00000000000..5eb1f537a86
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/addroles/AddDeveloperRoleCommandParser.java
@@ -0,0 +1,30 @@
+package seedu.address.logic.parser.addroles;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.addroles.AddDeveloperRoleCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses user input to create an AddDeveloperRoleCommand.
+ */
+public class AddDeveloperRoleCommandParser implements Parser {
+ /**
+ * Parses the given {@code args} and returns an AddDeveloperRoleCommand.
+ *
+ * @param args User input arguments.
+ * @return An AddDeveloperRoleCommand for adding a developer role.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ @Override
+ public AddDeveloperRoleCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ if (!args.isBlank()) {
+ String role = args.substring(1).trim();
+ return new AddDeveloperRoleCommand(role);
+ } else {
+ throw new ParseException("Role cannot be empty!");
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/delete/DeleteClientCommandParser.java b/src/main/java/seedu/address/logic/parser/delete/DeleteClientCommandParser.java
new file mode 100644
index 00000000000..beb710e7fd7
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/delete/DeleteClientCommandParser.java
@@ -0,0 +1,32 @@
+package seedu.address.logic.parser.delete;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.delete.DeleteClientCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteDeveloperCommand object
+ */
+public class DeleteClientCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteClientCommand
+ * and returns a DeleteClientCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteClientCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new DeleteClientCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteClientCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/delete/DeleteDeveloperCommandParser.java b/src/main/java/seedu/address/logic/parser/delete/DeleteDeveloperCommandParser.java
new file mode 100644
index 00000000000..1c4f732e626
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/delete/DeleteDeveloperCommandParser.java
@@ -0,0 +1,32 @@
+package seedu.address.logic.parser.delete;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.delete.DeleteDeveloperCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteDeveloperCommand object
+ */
+public class DeleteDeveloperCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteDeveloperCommand
+ * and returns a DeleteDeveloperCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteDeveloperCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new DeleteDeveloperCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteDeveloperCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/delete/DeleteProjectCommandParser.java b/src/main/java/seedu/address/logic/parser/delete/DeleteProjectCommandParser.java
new file mode 100644
index 00000000000..cc149c00105
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/delete/DeleteProjectCommandParser.java
@@ -0,0 +1,32 @@
+package seedu.address.logic.parser.delete;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.delete.DeleteProjectCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteDeveloperCommand object
+ */
+public class DeleteProjectCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the DeleteDeveloperCommand
+ * and returns a DeleteDeveloperCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public DeleteProjectCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new DeleteProjectCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteProjectCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/deleteroles/DeleteClientRoleCommandParser.java b/src/main/java/seedu/address/logic/parser/deleteroles/DeleteClientRoleCommandParser.java
new file mode 100644
index 00000000000..49441a2ca8d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/deleteroles/DeleteClientRoleCommandParser.java
@@ -0,0 +1,32 @@
+package seedu.address.logic.parser.deleteroles;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.deleteroles.DeleteClientRoleCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parser to handle user input and create a {@link DeleteClientRoleCommand}.
+ * This command is used to delete a client role by providing the role name as an argument.
+ * The role name should be a single word that follows the command.
+ */
+public class DeleteClientRoleCommandParser implements Parser {
+ /**
+ * Parses user input into a DeleteClientRoleCommand.
+ *
+ * @param args User input arguments.
+ * @return A DeleteClientRoleCommand to delete a client role.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ @Override
+ public DeleteClientRoleCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ if (!args.isBlank()) {
+ String role = args.substring(1).trim();
+ return new DeleteClientRoleCommand(role);
+ } else {
+ throw new ParseException("Role cannot be empty!");
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/deleteroles/DeleteDeveloperRoleCommandParser.java b/src/main/java/seedu/address/logic/parser/deleteroles/DeleteDeveloperRoleCommandParser.java
new file mode 100644
index 00000000000..20689a63f5c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/deleteroles/DeleteDeveloperRoleCommandParser.java
@@ -0,0 +1,32 @@
+package seedu.address.logic.parser.deleteroles;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.deleteroles.DeleteDeveloperRoleCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parser to handle user input and create a {@link DeleteDeveloperRoleCommand}.
+ * This command is used to delete a developer role by providing the role name as an argument.
+ * The role name should be a single word that follows the command.
+ */
+public class DeleteDeveloperRoleCommandParser implements Parser {
+ /**
+ * Parses user input and creates a {@link DeleteDeveloperRoleCommand} based on the provided role name.
+ *
+ * @param args User input arguments.
+ * @return A {@link DeleteDeveloperRoleCommand} for deleting a developer role.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ @Override
+ public DeleteDeveloperRoleCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ if (!args.isBlank()) {
+ String role = args.substring(1).trim();
+ return new DeleteDeveloperRoleCommand(role);
+ } else {
+ throw new ParseException("Role cannot be empty!");
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/edit/EditClientCommandParser.java b/src/main/java/seedu/address/logic/parser/edit/EditClientCommandParser.java
new file mode 100644
index 00000000000..ea916c1e94c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/edit/EditClientCommandParser.java
@@ -0,0 +1,124 @@
+package seedu.address.logic.parser.edit;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOCUMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GITHUBID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ORGANISATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.edit.EditClientCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.client.Client;
+
+/**
+ * Parses input arguments and creates a new EditClientCommand object
+ */
+public class EditClientCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the EditClientCommand
+ * and returns an EditClientCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+
+ public EditClientCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ADDRESS, PREFIX_PROJECT, PREFIX_DATEJOINED, PREFIX_ROLE, PREFIX_SALARY, PREFIX_GITHUBID,
+ PREFIX_RATING, PREFIX_ORGANISATION, PREFIX_DOCUMENT, PREFIX_DESCRIPTION, PREFIX_DEADLINE);
+
+ Index index;
+
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ EditClientCommand.MESSAGE_USAGE), pe);
+ }
+
+ if (!argMultimap.hasMappings()) {
+ throw new ParseException(EditClientCommand.MESSAGE_NOT_EDITED);
+ }
+
+ for (Prefix p : Client.UNUSED_PREFIXES) {
+ if (argMultimap.getValue(p).isPresent()) {
+ throw new ParseException(String.format(Messages.MESSAGE_INAPPLICABLE_PREFIX_USED,
+ EditClientCommand.MESSAGE_USAGE));
+ }
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_ORGANISATION, PREFIX_DOCUMENT, PREFIX_ROLE);
+
+ EditClientCommand.EditClientDescriptor editClientDescriptor = new EditClientCommand.EditClientDescriptor();
+
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+ editClientDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
+ }
+ if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
+ editClientDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
+ }
+ if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
+ editClientDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
+ }
+ if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
+ editClientDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
+ }
+ if (argMultimap.getValue(PREFIX_ROLE).isPresent()) {
+ editClientDescriptor.setRole(ParserUtil.parseClientRole(argMultimap.getValue(PREFIX_ROLE).get()));
+ }
+ if (argMultimap.getValue(PREFIX_ORGANISATION).isPresent()) {
+ editClientDescriptor.setOrganisation(ParserUtil.parseName(argMultimap.getValue(PREFIX_ORGANISATION).get()));
+ }
+ if (argMultimap.getValue(PREFIX_DOCUMENT).isPresent()) {
+ editClientDescriptor.setDocument(ParserUtil.parseDocument(argMultimap.getValue(PREFIX_DOCUMENT).get()));
+ }
+ //editClientDescriptor.setProjects(ParserUtil.parseProjectsWithCheck(argMultimap.getAllValues(PREFIX_PROJECT)));
+ parseProjectsForEdit(argMultimap.getAllValues(PREFIX_PROJECT)).ifPresent(editClientDescriptor::setProjects);
+
+ if (!editClientDescriptor.isAnyFieldEdited()) {
+ throw new ParseException(EditClientCommand.MESSAGE_NOT_EDITED);
+ }
+
+ return new EditClientCommand(index, editClientDescriptor);
+ }
+
+ /**
+ * Parses {@code Collection tags} into a {@code Set} if {@code projects} is non-empty.
+ * If {@code projects} contain only one element which is an empty string, it will be parsed into a
+ * {@code Set} containing zero tags.
+ */
+ private Optional> parseProjectsForEdit(Collection projects) throws ParseException {
+ assert projects != null;
+
+ if (projects.isEmpty()) {
+ return Optional.empty();
+ }
+ Collection projectSet =
+ projects.size() == 1 && projects.contains("") ? Collections.emptySet() : projects;
+ return Optional.of(ParserUtil.parseProjectsToSet(projectSet));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/edit/EditDeveloperCommandParser.java b/src/main/java/seedu/address/logic/parser/edit/EditDeveloperCommandParser.java
new file mode 100644
index 00000000000..60a06b8a044
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/edit/EditDeveloperCommandParser.java
@@ -0,0 +1,130 @@
+package seedu.address.logic.parser.edit;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOCUMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GITHUBID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ORGANISATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.edit.EditDeveloperCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.developer.Developer;
+
+/**
+ * Parses input arguments and creates a new EditDeveloperCommand object
+ */
+public class EditDeveloperCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the EditDeveloperCommand
+ * and returns an EditDeveloperCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+
+ public EditDeveloperCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ADDRESS, PREFIX_PROJECT, PREFIX_DATEJOINED, PREFIX_ROLE, PREFIX_SALARY, PREFIX_GITHUBID,
+ PREFIX_RATING, PREFIX_ORGANISATION, PREFIX_DOCUMENT, PREFIX_DESCRIPTION, PREFIX_DEADLINE);
+
+ Index index;
+
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ EditDeveloperCommand.MESSAGE_USAGE), pe);
+ }
+
+ if (!argMultimap.hasMappings()) {
+ throw new ParseException(EditDeveloperCommand.MESSAGE_NOT_EDITED);
+ }
+ for (Prefix p : Developer.UNUSED_PREFIXES) {
+ if (argMultimap.getValue(p).isPresent()) {
+ throw new ParseException(String.format(Messages.MESSAGE_INAPPLICABLE_PREFIX_USED,
+ EditDeveloperCommand.MESSAGE_USAGE));
+ }
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS,
+ PREFIX_DATEJOINED, PREFIX_ROLE, PREFIX_SALARY, PREFIX_GITHUBID, PREFIX_RATING);
+
+ EditDeveloperCommand.EditDeveloperDescriptor editDeveloperDescriptor =
+ new EditDeveloperCommand.EditDeveloperDescriptor();
+
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+ editDeveloperDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()));
+ }
+ if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
+ editDeveloperDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()));
+ }
+ if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
+ editDeveloperDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()));
+ }
+ if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
+ editDeveloperDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()));
+ }
+ if (argMultimap.getValue(PREFIX_DATEJOINED).isPresent()) {
+ editDeveloperDescriptor
+ .setDateJoined(ParserUtil.parseDateJoined(argMultimap.getValue(PREFIX_DATEJOINED).get()));
+ }
+ if (argMultimap.getValue(PREFIX_ROLE).isPresent()) {
+ editDeveloperDescriptor.setRole(ParserUtil.parseDeveloperRole(argMultimap.getValue(PREFIX_ROLE).get()));
+ }
+ if (argMultimap.getValue(PREFIX_SALARY).isPresent()) {
+ editDeveloperDescriptor.setSalary(ParserUtil.parseSalary(argMultimap.getValue(PREFIX_SALARY).get()));
+ }
+ if (argMultimap.getValue(PREFIX_GITHUBID).isPresent()) {
+ editDeveloperDescriptor.setGithubId(ParserUtil.parseGithubId(argMultimap.getValue(PREFIX_GITHUBID).get()));
+ }
+ if (argMultimap.getValue(PREFIX_RATING).isPresent()) {
+ editDeveloperDescriptor.setRating(ParserUtil.parseRating(argMultimap.getValue(PREFIX_RATING).get()));
+ }
+ parseProjectsForEdit(argMultimap.getAllValues(PREFIX_PROJECT)).ifPresent(editDeveloperDescriptor::setProjects);
+
+ if (!editDeveloperDescriptor.isAnyFieldEdited()) {
+ throw new ParseException(EditDeveloperCommand.MESSAGE_NOT_EDITED);
+ }
+
+ return new EditDeveloperCommand(index, editDeveloperDescriptor);
+ }
+
+ /**
+ * Parses {@code Collection tags} into a {@code Set} if {@code projects} is non-empty.
+ * If {@code projects} contain only one element which is an empty string, it will be parsed into a
+ * {@code Set} containing zero tags.
+ */
+ private Optional> parseProjectsForEdit(Collection projects) throws ParseException {
+ assert projects != null;
+
+ if (projects.isEmpty()) {
+ return Optional.empty();
+ }
+ Collection projectSet =
+ projects.size() == 1 && projects.contains("") ? Collections.emptySet() : projects;
+ return Optional.of(ParserUtil.parseProjectsToSet(projectSet));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/edit/EditProjectCommandParser.java b/src/main/java/seedu/address/logic/parser/edit/EditProjectCommandParser.java
new file mode 100644
index 00000000000..964ed18eff1
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/edit/EditProjectCommandParser.java
@@ -0,0 +1,106 @@
+package seedu.address.logic.parser.edit;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DOCUMENT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GITHUBID;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ORGANISATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.edit.EditProjectCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.project.Deadline;
+import seedu.address.model.project.Project;
+
+/**
+ * Parses input arguments and creates a new EditProjectCommand object
+ */
+public class EditProjectCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the EditProjectCommand
+ * and returns an EditProjectCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+
+ public EditProjectCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL,
+ PREFIX_ADDRESS, PREFIX_PROJECT, PREFIX_DATEJOINED, PREFIX_ROLE, PREFIX_SALARY, PREFIX_GITHUBID,
+ PREFIX_RATING, PREFIX_ORGANISATION, PREFIX_DOCUMENT, PREFIX_DESCRIPTION, PREFIX_DEADLINE);
+
+ Index index;
+
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ EditProjectCommand.MESSAGE_USAGE), pe);
+ }
+
+ if (!argMultimap.hasMappings()) {
+ throw new ParseException(EditProjectCommand.MESSAGE_NOT_EDITED);
+ }
+
+ for (Prefix p : Project.UNUSED_PREFIXES_FOR_EDIT) {
+ if (argMultimap.getValue(p).isPresent()) {
+ throw new ParseException(String.format(Messages.MESSAGE_INAPPLICABLE_PREFIX_USED,
+ EditProjectCommand.MESSAGE_USAGE));
+ }
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_DESCRIPTION);
+
+ EditProjectCommand.EditProjectDescriptor editProjectDescriptor = new EditProjectCommand.EditProjectDescriptor();
+
+ if (argMultimap.getValue(PREFIX_DESCRIPTION).isPresent()) {
+ editProjectDescriptor
+ .setDescription(ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get()));
+ }
+ parseDeadlinesForEdit(argMultimap.getAllValues(PREFIX_DEADLINE)).ifPresent(editProjectDescriptor::setDeadlines);
+
+ if (!editProjectDescriptor.isAnyFieldEdited()) {
+ throw new ParseException(EditProjectCommand.MESSAGE_NOT_EDITED);
+ }
+
+ return new EditProjectCommand(index, editProjectDescriptor);
+ }
+
+ /**
+ * Parses {@code Collection tags} into a {@code List} if {@code deadlines} is non-empty.
+ * If {@code deadlines} contain only one element which is an empty string, it will be parsed into a
+ * {@code List} containing zero tags.
+ */
+ private Optional> parseDeadlinesForEdit(Collection deadlines) throws ParseException {
+ assert deadlines != null;
+
+ if (deadlines.isEmpty()) {
+ return Optional.empty();
+ }
+ Collection deadlineSet =
+ deadlines.size() == 1 && deadlines.contains("") ? Collections.emptySet() : deadlines;
+ return Optional.of(ParserUtil.parseDeadlines(deadlineSet));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/find/FindClientCommandParser.java b/src/main/java/seedu/address/logic/parser/find/FindClientCommandParser.java
new file mode 100644
index 00000000000..4929098e1a8
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/find/FindClientCommandParser.java
@@ -0,0 +1,117 @@
+package seedu.address.logic.parser.find;
+
+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_DOCUMENT;
+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_ORGANISATION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.find.FindClientCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.client.AddressClientContainsKeywordsPredicate;
+import seedu.address.model.client.Client;
+import seedu.address.model.client.DocumentContainsKeywordsPredicate;
+import seedu.address.model.client.EmailClientContainsKeywordsPredicate;
+import seedu.address.model.client.NameClientContainsKeywordsPredicate;
+import seedu.address.model.client.OrganisationContainsKeywordsPredicate;
+import seedu.address.model.client.PhoneClientContainsKeywordsPredicate;
+import seedu.address.model.client.ProjectClientContainsKeywordsPredicate;
+import seedu.address.model.client.RoleClientContainsKeywordsPredicate;
+
+/**
+ * Parses input arguments and creates a new FindClientCommand object
+ */
+public class FindClientCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the FindClientCommand
+ * and returns a FindClientCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public FindClientCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindClientCommand.MESSAGE_USAGE));
+ }
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_ROLE, PREFIX_ADDRESS,
+ PREFIX_EMAIL, PREFIX_PHONE, PREFIX_PROJECT, PREFIX_DOCUMENT, PREFIX_ORGANISATION);
+
+ for (Prefix prefix : Arrays.asList(PREFIX_NAME, PREFIX_ROLE, PREFIX_ADDRESS,
+ PREFIX_EMAIL, PREFIX_PHONE, PREFIX_PROJECT, PREFIX_DOCUMENT, PREFIX_ORGANISATION)) {
+ if (argMultimap.getValue(prefix).isPresent() && argMultimap.getValue(prefix).get().isEmpty()) {
+ throw new ParseException("Please input a value after the prefix.");
+ }
+ }
+
+ if (!argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindClientCommand.MESSAGE_USAGE));
+ }
+
+ Predicate finalPredicate = buildPredicate(argMultimap);
+
+ return new FindClientCommand(finalPredicate);
+ }
+
+ private Predicate buildPredicate(ArgumentMultimap argMultimap) {
+ Predicate finalPredicate = client -> true;
+
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+ String[] nameKeywords = argMultimap.getValue(PREFIX_NAME).get().split("\\s+");
+ finalPredicate = finalPredicate.and(new NameClientContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_ROLE).isPresent()) {
+ String[] roleKeywords = argMultimap.getValue(PREFIX_ROLE).get().split("\\s+");
+ finalPredicate = finalPredicate.and(new RoleClientContainsKeywordsPredicate(Arrays.asList(roleKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
+ String[] addressKeywords = argMultimap.getValue(PREFIX_ADDRESS).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new AddressClientContainsKeywordsPredicate(Arrays.asList(addressKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
+ String[] emailKeywords = argMultimap.getValue(PREFIX_EMAIL).get().split("\\s+");
+ finalPredicate = finalPredicate.and(new EmailClientContainsKeywordsPredicate(Arrays.asList(emailKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
+ String[] phoneKeywords = argMultimap.getValue(PREFIX_PHONE).get().split("\\s+");
+ finalPredicate = finalPredicate.and(new PhoneClientContainsKeywordsPredicate(Arrays.asList(phoneKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_PROJECT).isPresent()) {
+ String[] projectKeywords = argMultimap.getValue(PREFIX_PROJECT).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new ProjectClientContainsKeywordsPredicate(Arrays.asList(projectKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_DOCUMENT).isPresent()) {
+ String[] documentKeywords = argMultimap.getValue(PREFIX_DOCUMENT).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new DocumentContainsKeywordsPredicate(Arrays.asList(documentKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_ORGANISATION).isPresent()) {
+ String[] organisationKeywords = argMultimap.getValue(PREFIX_ORGANISATION).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new OrganisationContainsKeywordsPredicate(Arrays.asList(organisationKeywords)));
+ }
+ return finalPredicate;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/find/FindDeadlineCommandParser.java b/src/main/java/seedu/address/logic/parser/find/FindDeadlineCommandParser.java
new file mode 100644
index 00000000000..c54ff932e0a
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/find/FindDeadlineCommandParser.java
@@ -0,0 +1,85 @@
+package seedu.address.logic.parser.find;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PRIORITY;
+
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.find.FindDeadlineCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.commons.Date;
+import seedu.address.model.project.Deadline;
+import seedu.address.model.project.Priority;
+
+/**
+ * Parser to handle user input and create a {@link FindDeadlineCommand}.
+ * This command is used to search and filter deadlines within projects based on specific criteria.
+ * The supported criteria include filtering by date joined and priority level.
+ */
+public class FindDeadlineCommandParser implements Parser {
+ /**
+ * Parses user input and creates a {@link FindDeadlineCommand} based on the provided criteria.
+ *
+ * @param args User input arguments, which may include criteria for filtering deadlines.
+ * @return A {@link FindDeadlineCommand} for searching and filtering deadlines.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ public FindDeadlineCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindDeadlineCommand.MESSAGE_USAGE));
+ }
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_DATEJOINED, PREFIX_PRIORITY);
+
+ for (Prefix prefix : Arrays.asList(PREFIX_DATEJOINED, PREFIX_PRIORITY)) {
+ if (argMultimap.getValue(prefix).isPresent() && argMultimap.getValue(prefix).get().isEmpty()) {
+ throw new ParseException("Please input a value after the prefix.");
+ }
+ }
+
+ if (!argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindDeadlineCommand.MESSAGE_USAGE));
+ }
+
+ Predicate finalPredicate = buildPredicate(argMultimap);
+
+ return new FindDeadlineCommand(finalPredicate);
+ }
+
+ /**
+ * Builds a predicate for filtering deadlines based on the criteria provided in the user input.
+ *
+ * @param argMultimap Argument multimap containing user input arguments.
+ * @return A predicate for filtering deadlines.
+ */
+ private Predicate buildPredicate(ArgumentMultimap argMultimap) throws ParseException {
+ Predicate finalPredicate = deadline -> true;
+
+ if (argMultimap.getValue(PREFIX_DATEJOINED).isPresent()) {
+ String dateKeywords = argMultimap.getValue(PREFIX_DATEJOINED).get();
+ Date input = ParserUtil.parseDateDeadline(dateKeywords);
+ finalPredicate =
+ finalPredicate.and(d -> !d.getDate().value.after(input.value));
+ // Replace with your DateJoinedPredicate
+ }
+
+ if (argMultimap.getValue(PREFIX_PRIORITY).isPresent()) {
+ String priorityKeywords = argMultimap.getValue(PREFIX_PRIORITY).get();
+ Priority input = ParserUtil.parsePriority(priorityKeywords);
+ finalPredicate = finalPredicate.and(d -> d.getPriority().equals(input));
+ // Replace with your PriorityPredicate
+ }
+
+ return finalPredicate;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/find/FindDeveloperCommandParser.java b/src/main/java/seedu/address/logic/parser/find/FindDeveloperCommandParser.java
new file mode 100644
index 00000000000..d3f325c40a1
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/find/FindDeveloperCommandParser.java
@@ -0,0 +1,150 @@
+package seedu.address.logic.parser.find;
+
+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_DATEJOINED;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GITHUBID;
+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_PROJECT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_RATING;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_ROLE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_SALARY;
+
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.find.FindDeveloperCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.developer.AddressDeveloperContainsKeywordsPredicate;
+import seedu.address.model.developer.DateJoinedContainsKeywordsPredicate;
+import seedu.address.model.developer.Developer;
+import seedu.address.model.developer.EmailDeveloperContainsKeywordsPredicate;
+import seedu.address.model.developer.GithubIdContainsKeywordsPredicate;
+import seedu.address.model.developer.NameDeveloperContainsKeywordsPredicate;
+import seedu.address.model.developer.PhoneDeveloperContainsKeywordsPredicate;
+import seedu.address.model.developer.ProjectDeveloperContainsKeywordsPredicate;
+import seedu.address.model.developer.RatingContainsKeywordsPredicate;
+import seedu.address.model.developer.RoleDeveloperContainsKeywordsPredicate;
+import seedu.address.model.developer.SalaryContainsKeywordsPredicate;
+
+/**
+ * Parser to handle user input and create a {@link FindDeveloperCommand}.
+ * This command is used to search and filter developers based on specific criteria.
+ * The supported criteria include filtering by name, role, address, email, GitHub ID, date joined,
+ * projects, phone number, salary, rating, and GitHub ID.
+ */
+public class FindDeveloperCommandParser implements Parser {
+ /**
+ * Parses user input and creates a {@link FindDeveloperCommand} based on the provided criteria.
+ *
+ * @param args User input arguments, which may include criteria for filtering developers.
+ * @return A {@link FindDeveloperCommand} for searching and filtering developers.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ public FindDeveloperCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ FindDeveloperCommand.MESSAGE_USAGE));
+ }
+
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_ROLE, PREFIX_ADDRESS,
+ PREFIX_EMAIL, PREFIX_GITHUBID, PREFIX_DATEJOINED,
+ PREFIX_PROJECT, PREFIX_PHONE, PREFIX_SALARY, PREFIX_RATING);
+
+ for (Prefix prefix : Arrays.asList(PREFIX_NAME, PREFIX_ROLE, PREFIX_ADDRESS,
+ PREFIX_EMAIL, PREFIX_GITHUBID, PREFIX_DATEJOINED,
+ PREFIX_PROJECT, PREFIX_PHONE, PREFIX_SALARY, PREFIX_RATING)) {
+ if (argMultimap.getValue(prefix).isPresent() && argMultimap.getValue(prefix).get().isEmpty()) {
+ throw new ParseException("Please input a value after the prefix.");
+ }
+ }
+
+ if (!argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ FindDeveloperCommand.MESSAGE_USAGE));
+ }
+
+ Predicate finalPredicate = buildPredicate(argMultimap);
+
+ return new FindDeveloperCommand(finalPredicate);
+ }
+
+ /**
+ * Builds a predicate for filtering developers based on the criteria provided in the user input.
+ *
+ * @param argMultimap Argument multimap containing user input arguments.
+ * @return A predicate for filtering developers.
+ */
+ private Predicate buildPredicate(ArgumentMultimap argMultimap) {
+ Predicate finalPredicate = developer -> true;
+
+ if (argMultimap.getValue(PREFIX_NAME).isPresent()) {
+ String[] nameKeywords = argMultimap.getValue(PREFIX_NAME).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new NameDeveloperContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_ROLE).isPresent()) {
+ String[] roleKeywords = argMultimap.getValue(PREFIX_ROLE).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new RoleDeveloperContainsKeywordsPredicate(Arrays.asList(roleKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) {
+ String[] addressKeywords = argMultimap.getValue(PREFIX_ADDRESS).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new AddressDeveloperContainsKeywordsPredicate(Arrays.asList(addressKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_DATEJOINED).isPresent()) {
+ String[] dateJoinedKeywords = argMultimap.getValue(PREFIX_DATEJOINED).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new DateJoinedContainsKeywordsPredicate(Arrays.asList(dateJoinedKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) {
+ String[] emailKeywords = argMultimap.getValue(PREFIX_EMAIL).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new EmailDeveloperContainsKeywordsPredicate(Arrays.asList(emailKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_PHONE).isPresent()) {
+ String[] phoneKeywords = argMultimap.getValue(PREFIX_PHONE).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new PhoneDeveloperContainsKeywordsPredicate(Arrays.asList(phoneKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_PROJECT).isPresent()) {
+ String[] projectKeywords = argMultimap.getValue(PREFIX_PROJECT).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new ProjectDeveloperContainsKeywordsPredicate(Arrays.asList(projectKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_SALARY).isPresent()) {
+ String[] salaryKeywords = argMultimap.getValue(PREFIX_SALARY).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new SalaryContainsKeywordsPredicate(Arrays.asList(salaryKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_RATING).isPresent()) {
+ String[] ratingKeywords = argMultimap.getValue(PREFIX_RATING).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new RatingContainsKeywordsPredicate(Arrays.asList(ratingKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_GITHUBID).isPresent()) {
+ String[] githubKeywords = argMultimap.getValue(PREFIX_GITHUBID).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new GithubIdContainsKeywordsPredicate(Arrays.asList(githubKeywords)));
+ }
+
+ return finalPredicate;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/find/FindProjectCommandParser.java b/src/main/java/seedu/address/logic/parser/find/FindProjectCommandParser.java
new file mode 100644
index 00000000000..cf555f66bf7
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/find/FindProjectCommandParser.java
@@ -0,0 +1,91 @@
+package seedu.address.logic.parser.find;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DEADLINE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_PROJECT;
+
+import java.util.Arrays;
+import java.util.function.Predicate;
+
+import seedu.address.logic.commands.find.FindProjectCommand;
+import seedu.address.logic.parser.ArgumentMultimap;
+import seedu.address.logic.parser.ArgumentTokenizer;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.Prefix;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.project.DeadlineContainsKeywordsPredicate;
+import seedu.address.model.project.DescriptionContainsKeywordsPredicate;
+import seedu.address.model.project.Project;
+import seedu.address.model.project.ProjectNameContainsKeywordsPredicate;
+
+/**
+ * Parser to handle user input and create a {@link FindProjectCommand}.
+ * This command is used to search and filter projects based on specific criteria.
+ * The supported criteria include filtering by project name, description, and deadlines.
+ */
+public class FindProjectCommandParser implements Parser {
+
+ /**
+ * Parses user input and creates a {@link FindProjectCommand} based on the provided criteria.
+ *
+ * @param args User input arguments, which may include criteria for filtering projects.
+ * @return A {@link FindProjectCommand} for searching and filtering projects.
+ * @throws ParseException If the user input does not conform to the expected format.
+ */
+ public FindProjectCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindProjectCommand.MESSAGE_USAGE));
+ }
+
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_DEADLINE, PREFIX_DESCRIPTION, PREFIX_PROJECT);
+
+ for (Prefix prefix : Arrays.asList(PREFIX_DEADLINE, PREFIX_DESCRIPTION, PREFIX_PROJECT)) {
+ if (argMultimap.getValue(prefix).isPresent() && argMultimap.getValue(prefix).get().isEmpty()) {
+ throw new ParseException("Please input a value after the prefix.");
+ }
+ }
+
+ if (!argMultimap.getPreamble().isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindProjectCommand.MESSAGE_USAGE));
+ }
+
+ Predicate finalPredicate = buildPredicate(argMultimap);
+
+ return new FindProjectCommand(finalPredicate);
+ }
+
+ /**
+ * Builds a predicate for filtering projects based on the criteria provided in the user input.
+ *
+ * @param argMultimap Argument multimap containing user input arguments.
+ * @return A predicate for filtering projects.
+ */
+ private Predicate buildPredicate(ArgumentMultimap argMultimap) {
+ Predicate finalPredicate = project -> true;
+
+ if (argMultimap.getValue(PREFIX_PROJECT).isPresent()) {
+ String[] projectNameKeywords = argMultimap.getValue(PREFIX_PROJECT).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new ProjectNameContainsKeywordsPredicate(Arrays.asList(projectNameKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_DESCRIPTION).isPresent()) {
+ String[] descriptionKeywords = argMultimap.getValue(PREFIX_DESCRIPTION).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new DescriptionContainsKeywordsPredicate(Arrays.asList(descriptionKeywords)));
+ }
+
+ if (argMultimap.getValue(PREFIX_DEADLINE).isPresent()) {
+ String[] deadlineKeywords = argMultimap.getValue(PREFIX_DEADLINE).get().split("\\s+");
+ finalPredicate =
+ finalPredicate.and(new DeadlineContainsKeywordsPredicate(Arrays.asList(deadlineKeywords)));
+ }
+
+ return finalPredicate;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/imports/ImportClientCommandParser.java b/src/main/java/seedu/address/logic/parser/imports/ImportClientCommandParser.java
new file mode 100644
index 00000000000..63c5d7b6640
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/imports/ImportClientCommandParser.java
@@ -0,0 +1,100 @@
+package seedu.address.logic.parser.imports;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_FILE;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Set;
+
+import seedu.address.logic.commands.imports.ImportClientCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.client.Client;
+import seedu.address.model.client.ClientRoles;
+import seedu.address.model.client.Document;
+import seedu.address.model.commons.Name;
+import seedu.address.model.person.Address;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Phone;
+
+/**
+ * Parses input arguments and creates a
+ * new {@link ImportClientCommand} object for importing client data from a CSV file.
+ * The CSV file should contain columns with specific column names and data in a certain format.
+ */
+public class ImportClientCommandParser implements Parser {
+
+ /**
+ * Parses the provided file name to import client data from a CSV file.
+ *
+ * @param fileName The name of the CSV file to import client data from.
+ * @return A {@link ImportClientCommand} for importing client data.
+ * @throws ParseException If there are issues with file handling or if the file format is invalid.
+ */
+ @Override
+ public ImportClientCommand parse(String fileName) throws ParseException {
+ try {
+ FileWriter myWriter = new FileWriter("filename.txt");
+ myWriter.write("Files in Java might be tricky, but it is fun enough!");
+ myWriter.close();
+ fileName = fileName.trim();
+ BufferedReader br = new BufferedReader(new FileReader(fileName));
+ String line = "";
+ String splitBy = ",";
+
+ // Check if the CSV file contains valid column names
+ boolean isValid = checkColumnNames(br.readLine());
+ if (!isValid) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ImportClientCommand.MESSAGE_USAGE));
+ }
+
+ ArrayList toAddList = new ArrayList<>();
+ while ((line = br.readLine()) != null) {
+ String[] clientData = line.split(splitBy);
+
+ // Parse client data from CSV columns
+ Name name = ParserUtil.parseName(clientData[0]);
+ Phone phone = ParserUtil.parsePhone(clientData[1]);
+ Email email = ParserUtil.parseEmail(clientData[2]);
+ Address address = ParserUtil.parseAddress(clientData[3]);
+ ClientRoles role = ParserUtil.parseClientRole(clientData[4]);
+ Name organisation = ParserUtil.parseName(clientData[5]);
+ Document document = ParserUtil.parseDocument(clientData[6]);
+ ArrayList projects = new ArrayList<>();
+ for (int i = 7; i < clientData.length; i++) {
+ projects.add(clientData[i]);
+ }
+ Set projectList = ParserUtil.parseProjectsToSet(projects);
+
+ // Create a Client object
+ Client client = new Client(name, phone, email, address, role, projectList, organisation, document);
+
+ // Add the client to the list
+ toAddList.add(client);
+ }
+
+ return new ImportClientCommand(toAddList);
+ } catch (FileNotFoundException ex) {
+ throw new ParseException(MESSAGE_INVALID_FILE);
+ } catch (IOException e) {
+ throw new ParseException("Error reading line from file " + fileName);
+ }
+ }
+
+ // Define a method to check if the CSV file contains valid column names
+ private boolean checkColumnNames(String line) {
+ String[] columnNames = line.split(",");
+ return columnNames.length == 8 && columnNames[0].contains("Name")
+ && columnNames[1].contains("Contact Number") && columnNames[2].contains("Email")
+ && columnNames[3].contains("Address") && columnNames[4].contains("Role")
+ && columnNames[5].contains("Organisation") && columnNames[6].contains("Document")
+ && columnNames[7].contains("Projects");
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/imports/ImportDeveloperCommandParser.java b/src/main/java/seedu/address/logic/parser/imports/ImportDeveloperCommandParser.java
new file mode 100644
index 00000000000..6704b89ffdc
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/imports/ImportDeveloperCommandParser.java
@@ -0,0 +1,126 @@
+package seedu.address.logic.parser.imports;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_FILE;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Set;
+
+import seedu.address.logic.commands.imports.ImportDeveloperCommand;
+import seedu.address.logic.parser.Parser;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.commons.Date;
+import seedu.address.model.commons.Name;
+import seedu.address.model.developer.Developer;
+import seedu.address.model.developer.DeveloperRoles;
+import seedu.address.model.developer.GithubId;
+import seedu.address.model.developer.Rating;
+import seedu.address.model.developer.Salary;
+import seedu.address.model.person.Address;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Phone;
+
+/**
+ * Parses input arguments and
+ * creates a new {@link ImportDeveloperCommand} object for importing developer data from a CSV file.
+ * The CSV file should contain columns with specific column names and data in a certain format.
+ */
+public class ImportDeveloperCommandParser implements Parser {
+
+ /**
+ * Parses the provided file name to import developer data from a CSV file.
+ *
+ * @param fileName The name of the CSV file to import developer data from.
+ * @return A {@link ImportDeveloperCommand} for importing developer data.
+ * @throws ParseException If there are issues with file handling or if the file format is invalid.
+ */
+ @Override
+ public ImportDeveloperCommand parse(String fileName) throws ParseException {
+ try {
+ fileName = fileName.trim();
+ BufferedReader br = new BufferedReader(new FileReader(fileName));
+ String line = "";
+ String splitBy = ",";
+ boolean isValid = checkColumnNames(br.readLine());
+ if (!isValid) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ImportDeveloperCommand.MESSAGE_USAGE));
+ }
+ ArrayList toAddList = new ArrayList<>();
+ while ((line = br.readLine()) != null) {
+ String[] employee = line.split(splitBy); // use comma as separator
+ Name name = ParserUtil.parseName(employee[0]);
+ Phone phone = ParserUtil.parsePhone(employee[1]);
+ Email email = ParserUtil.parseEmail(employee[2]);
+ Address address = ParserUtil.parseAddress(employee[3]);
+ Date dateJoined = ParserUtil.parseDateJoined(employee[4]);
+ DeveloperRoles role = ParserUtil.parseDeveloperRole(employee[5]);
+ Salary salary = ParserUtil.parseSalary(employee[6]);
+ GithubId githubId = ParserUtil.parseGithubId(employee[7]);
+ Rating rating = ParserUtil.parseRating(employee[8]);
+ ArrayList projects = new ArrayList<>();
+ for (int i = 9; i < employee.length; i++) {
+ projects.add(employee[i]);
+ }
+ Set projectList = ParserUtil.parseProjectsToSet(projects);
+
+ Developer developer =
+ new Developer(name, phone, email,
+ address, role, projectList, salary, dateJoined, githubId, rating);
+ toAddList.add(developer);
+
+ }
+ return new ImportDeveloperCommand(toAddList);
+ } catch (FileNotFoundException ex) {
+ throw new ParseException(MESSAGE_INVALID_FILE);
+ } catch (IOException e) {
+ throw new ParseException("Error reading line from file " + fileName);
+ }
+ }
+
+ /**
+ * Checks if the CSV file contains valid column names based on expected format.
+ *
+ * @param line The header line of the CSV file.
+ * @return True if the column names match the expected format, false otherwise.
+ */
+ private boolean checkColumnNames(String line) {
+ String[] columnNames = line.split(",");
+ if (!columnNames[0].contains("Name")) {
+ return false;
+ }
+ if (!columnNames[1].contains("Contact Number")) {
+ return false;
+ }
+ if (!columnNames[2].contains("Email")) {
+ return false;
+ }
+ if (!columnNames[3].contains("Address")) {
+ return false;
+ }
+ if (!columnNames[4].contains("Date Joined")) {
+ return false;
+ }
+ if (!columnNames[5].contains("Role")) {
+ return false;
+ }
+ if (!columnNames[6].contains("Salary")) {
+ return false;
+ }
+ if (!columnNames[7].contains("GithubId")) {
+ return false;
+ }
+ if (!columnNames[8].contains("Rating")) {
+ return false;
+ }
+ if (!columnNames[9].contains("Projects")) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/mark/MarkDeadlineCommandParser.java b/src/main/java/seedu/address/logic/parser/mark/MarkDeadlineCommandParser.java
new file mode 100644
index 00000000000..ef7ad5345dc
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/mark/MarkDeadlineCommandParser.java
@@ -0,0 +1,44 @@
+package seedu.address.logic.parser.mark;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.mark.MarkDeadlineCommand;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new MarkDeadlineCommand object.
+ */
+public class MarkDeadlineCommandParser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the MarkDeadlineCommand
+ * and returns a MarkDeadlineCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format.
+ */
+ public MarkDeadlineCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ String[] input = args.trim().split(" ");
+
+ if (input.length != 2) {
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ MarkDeadlineCommand.MESSAGE_USAGE));
+ }
+
+ Index projIndex;
+ Index deadlineIndex;
+
+ try {
+ projIndex = ParserUtil.parseIndex(input[0]);
+ deadlineIndex = ParserUtil.parseIndex(input[1]);
+ } catch (NumberFormatException e) {
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ MarkDeadlineCommand.MESSAGE_USAGE));
+ }
+
+ return new MarkDeadlineCommand(projIndex, deadlineIndex);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/mark/UnmarkDeadlineCommandParser.java b/src/main/java/seedu/address/logic/parser/mark/UnmarkDeadlineCommandParser.java
new file mode 100644
index 00000000000..7808fb7bab3
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/mark/UnmarkDeadlineCommandParser.java
@@ -0,0 +1,43 @@
+package seedu.address.logic.parser.mark;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.mark.UnmarkDeadlineCommand;
+import seedu.address.logic.parser.ParserUtil;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new UnmarkDeadlineCommand object.
+ */
+public class UnmarkDeadlineCommandParser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the UnmarkDeadlineCommand
+ * and returns a UnmarkDeadlineCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format.
+ */
+ public UnmarkDeadlineCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ String[] input = args.trim().split(" ");
+
+ if (input.length != 2) {
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ UnmarkDeadlineCommand.MESSAGE_USAGE));
+ }
+
+ Index projIndex;
+ Index deadlineIndex;
+
+ try {
+ projIndex = ParserUtil.parseIndex(input[0]);
+ deadlineIndex = ParserUtil.parseIndex(input[1]);
+ } catch (NumberFormatException e) {
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT,
+ UnmarkDeadlineCommand.MESSAGE_USAGE));
+ }
+
+ return new UnmarkDeadlineCommand(projIndex, deadlineIndex);
+ }
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 73397161e84..2c0716a91c7 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -3,109 +3,291 @@
import static java.util.Objects.requireNonNull;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
import javafx.collections.ObservableList;
import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.client.Client;
+import seedu.address.model.client.UniqueClientList;
+import seedu.address.model.developer.Developer;
+import seedu.address.model.developer.UniqueDeveloperList;
import seedu.address.model.person.Person;
-import seedu.address.model.person.UniquePersonList;
+import seedu.address.model.project.Project;
+import seedu.address.model.project.UniqueProjectList;
/**
- * Wraps all data at the address-book level
- * Duplicates are not allowed (by .isSamePerson comparison)
+ * Represents the entire address book. Contains the data of the developers, clients, and projects.
+ * Duplicates are not allowed (by .isSamePerson comparison).
*/
public class AddressBook implements ReadOnlyAddressBook {
- private final UniquePersonList persons;
+ private final UniqueDeveloperList developers;
+ private final UniqueClientList clients;
+ private final UniqueProjectList projects;
- /*
- * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
- * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
- *
- * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication
- * among constructors.
- */
{
- persons = new UniquePersonList();
+ developers = new UniqueDeveloperList();
+ clients = new UniqueClientList();
+ projects = new UniqueProjectList();
}
- public AddressBook() {}
+ /**
+ * Creates an empty AddressBook.
+ */
+ public AddressBook() {
+ }
/**
- * Creates an AddressBook using the Persons in the {@code toBeCopied}
+ * Creates an AddressBook using the data in the given ReadOnlyAddressBook.
+ *
+ * @param toBeCopied The ReadOnlyAddressBook containing data to be copied into this AddressBook.
*/
public AddressBook(ReadOnlyAddressBook toBeCopied) {
this();
resetData(toBeCopied);
}
- //// list overwrite operations
+ /// Project Validation
+
+ /**
+ * Checks if the projects assigned to a person are valid.
+ *
+ * @param person The person to check for valid projects.
+ * @return The name of the first invalid project if any, else returns null.
+ */
+ public String areProjectsValid(Person person) {
+ Set projects = person.getProjects();
+ for (String p : projects) {
+ if (!this.hasProject(p)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ // List Overwrite Operations
+
+ /**
+ * Sets the list of developers in the AddressBook with the provided list.
+ *
+ * @param developers The list of developers to set.
+ */
+ public void setDevelopers(List developers) {
+ this.developers.setDevelopers(developers);
+ }
+
+ /**
+ * Sets the list of clients in the AddressBook with the provided list.
+ *
+ * @param clients The list of clients to set.
+ */
+ public void setClients(List clients) {
+ this.clients.setClients(clients);
+ }
/**
- * Replaces the contents of the person list with {@code persons}.
- * {@code persons} must not contain duplicate persons.
+ * Sets the list of projects in the AddressBook with the provided list.
+ *
+ * @param projects The list of projects to set.
*/
- public void setPersons(List persons) {
- this.persons.setPersons(persons);
+ public void setProjects(List projects) {
+ this.projects.setProjects(projects);
}
/**
- * Resets the existing data of this {@code AddressBook} with {@code newData}.
+ * Resets the data of this AddressBook with data from another ReadOnlyAddressBook.
+ *
+ * @param newData The ReadOnlyAddressBook containing data to reset this AddressBook with.
*/
public void resetData(ReadOnlyAddressBook newData) {
requireNonNull(newData);
+ setDevelopers(newData.getDeveloperList());
+ setClients(newData.getClientList());
+ setProjects(newData.getProjectList());
+ }
- setPersons(newData.getPersonList());
+ // Developer-level Operations
+
+ /**
+ * Checks if a developer exists in this AddressBook.
+ *
+ * @param developer The developer to check.
+ * @return True if the developer exists, false otherwise.
+ */
+ public boolean hasDeveloper(Developer developer) {
+ requireNonNull(developer);
+ return developers.contains(developer);
}
- //// person-level operations
+ /**
+ * Adds a developer to this AddressBook.
+ *
+ * @param developer The developer to add.
+ */
+ public void addDeveloper(Developer developer) {
+ developers.add(developer);
+ }
/**
- * Returns true if a person with the same identity as {@code person} exists in the address book.
+ * Sets an existing developer in this AddressBook with an edited developer.
+ *
+ * @param target The developer to be replaced.
+ * @param editedDeveloper The edited developer.
*/
- public boolean hasPerson(Person person) {
- requireNonNull(person);
- return persons.contains(person);
+ public void setDeveloper(Developer target, Developer editedDeveloper) {
+ requireNonNull(editedDeveloper);
+ developers.setDeveloper(target, editedDeveloper);
}
/**
- * Adds a person to the address book.
- * The person must not already exist in the address book.
+ * Removes a developer from this AddressBook.
+ *
+ * @param key The developer to remove.
*/
- public void addPerson(Person p) {
- persons.add(p);
+ public void removeDeveloper(Developer key) {
+ developers.remove(key);
}
+ // Client-level Operations
+
/**
- * Replaces the given person {@code target} in the list with {@code editedPerson}.
- * {@code target} must exist in the address book.
- * The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
+ * Checks if a client exists in this AddressBook.
+ *
+ * @param client The client to check.
+ * @return True if the client exists, false otherwise.
*/
- public void setPerson(Person target, Person editedPerson) {
- requireNonNull(editedPerson);
+ public boolean hasClient(Client client) {
+ requireNonNull(client);
+ return clients.contains(client);
+ }
- persons.setPerson(target, editedPerson);
+ /**
+ * Adds a client to this AddressBook.
+ *
+ * @param client The client to add.
+ */
+ public void addClient(Client client) {
+ clients.add(client);
}
/**
- * Removes {@code key} from this {@code AddressBook}.
- * {@code key} must exist in the address book.
+ * Sets an existing client in this AddressBook with an edited client.
+ *
+ * @param target The client to be replaced.
+ * @param editedClient The edited client.
*/
- public void removePerson(Person key) {
- persons.remove(key);
+ public void setClient(Client target, Client editedClient) {
+ requireNonNull(editedClient);
+ clients.setClient(target, editedClient);
}
- //// util methods
+ /**
+ * Removes a client from this AddressBook.
+ *
+ * @param key The client to remove.
+ */
+ public void removeClient(Client key) {
+ clients.remove(key);
+ }
+
+ // Project-level Operations
+
+ /**
+ * Checks if a project exists in this AddressBook.
+ *
+ * @param project The project to check.
+ * @return True if the project exists, false otherwise.
+ */
+ public boolean hasProject(Project project) {
+ requireNonNull(project);
+ return projects.contains(project);
+ }
+
+ /**
+ * Checks if a project with a given name exists in this AddressBook.
+ *
+ * @param project The name of the project to check.
+ * @return True if the project exists, false otherwise.
+ */
+ public boolean hasProject(String project) {
+ requireNonNull(project);
+ return projects.contains(project);
+ }
+ /**
+ * Adds a project to this AddressBook.
+ *
+ * @param project The project to add.
+ */
+ public void addProject(Project project) {
+ projects.add(project);
+ }
+
+ /**
+ * Sets an existing project in this AddressBook with an edited project.
+ *
+ * @param target The project to be replaced.
+ * @param editedProject The edited project.
+ */
+ public void setProject(Project target, Project editedProject) {
+ requireNonNull(editedProject);
+ projects.setProject(target, editedProject);
+ }
+
+ /**
+ * Removes a project from this AddressBook. Also updates clients and developers to remove the project assignment.
+ *
+ * @param key The project to remove.
+ */
+ public void removeProject(Project key) {
+ projects.remove(key);
+ clients.updateClientProjects(key.getName());
+ developers.updateDeveloperProjects(key.getName());
+ }
+
+ /**
+ * Returns a string representation of this AddressBook.
+ *
+ * @return A string representation of this AddressBook.
+ */
@Override
public String toString() {
return new ToStringBuilder(this)
- .add("persons", persons)
+ .add("developers", developers)
+ .add("clients", clients)
+ .add("projects", projects)
.toString();
}
+ /**
+ * Returns the list of developers as an observable list.
+ *
+ * @return The list of developers as an observable list.
+ */
+ @Override
+ public ObservableList getDeveloperList() {
+ return developers.asUnmodifiableObservableList();
+ }
+
+ /**
+ * Returns the list of clients as an observable list.
+ *
+ * @return The list of clients as an observable list.
+ */
+ @Override
+ public ObservableList getClientList() {
+ return clients.asUnmodifiableObservableList();
+ }
+
+ /**
+ * Returns the list of projects as an observable list.
+ *
+ * @return The list of projects as an observable list.
+ */
@Override
- public ObservableList getPersonList() {
- return persons.asUnmodifiableObservableList();
+ public ObservableList getProjectList() {
+ return projects.asUnmodifiableObservableList();
}
@Override
@@ -114,17 +296,18 @@ public boolean equals(Object other) {
return true;
}
- // instanceof handles nulls
if (!(other instanceof AddressBook)) {
return false;
}
AddressBook otherAddressBook = (AddressBook) other;
- return persons.equals(otherAddressBook.persons);
+ return developers.equals(otherAddressBook.developers)
+ && clients.equals(otherAddressBook.clients)
+ && projects.equals(otherAddressBook.projects);
}
@Override
public int hashCode() {
- return persons.hashCode();
+ return Objects.hash(developers, clients, projects);
}
}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..3e8206f2b6c 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -5,25 +5,38 @@
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.client.Client;
+import seedu.address.model.developer.Developer;
import seedu.address.model.person.Person;
+import seedu.address.model.project.Deadline;
+import seedu.address.model.project.Project;
/**
* The API of the Model component.
*/
public interface Model {
- /** {@code Predicate} that always evaluate to true */
- Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
-
/**
- * Replaces user prefs data with the data in {@code userPrefs}.
+ * {@code Predicate} that always evaluate to true
*/
- void setUserPrefs(ReadOnlyUserPrefs userPrefs);
+ Predicate PREDICATE_SHOW_ALL_DEVELOPERS = unused -> true;
+ Predicate PREDICATE_SHOW_ALL_CLIENTS = unused -> true;
+ Predicate PREDICATE_SHOW_ALL_PROJECTS = unused -> true;
+ Predicate PREDICATE_SHOW_NO_DEVELOPER = unused -> false;
+ Predicate PREDICATE_SHOW_NO_CLIENT = unused -> false;
+ Predicate PREDICATE_SHOW_NO_PROJECT = unused -> false;
/**
* Returns the user prefs.
*/
ReadOnlyUserPrefs getUserPrefs();
+ /**
+ * Replaces user prefs data with the data in {@code userPrefs}.
+ */
+ void setUserPrefs(ReadOnlyUserPrefs userPrefs);
+
/**
* Returns the user prefs' GUI settings.
*/
@@ -44,44 +57,98 @@ public interface Model {
*/
void setAddressBookFilePath(Path addressBookFilePath);
+ /**
+ * Returns the AddressBook
+ */
+ ReadOnlyAddressBook getAddressBook();
+
/**
* Replaces address book data with the data in {@code addressBook}.
*/
void setAddressBook(ReadOnlyAddressBook addressBook);
- /** Returns the AddressBook */
- ReadOnlyAddressBook getAddressBook();
-
/**
* Returns true if a person with the same identity as {@code person} exists in the address book.
*/
- boolean hasPerson(Person person);
+ boolean hasDeveloper(Developer person);
+
+ boolean hasClient(Client client);
+
+ boolean hasProject(seedu.address.model.project.Project project);
+
+ /**
+ * Returns null if the projects assigned to a person exist and are valid,
+ * returns the invalid project name otherwise.
+ *
+ * @param person The person to check.
+ * @returns The String of the invalid project name, or null if all projects are valid.
+ */
+ String areProjectsValid(Person person);
/**
* Deletes the given person.
* The person must exist in the address book.
*/
- void deletePerson(Person target);
+ void deleteDeveloper(Developer target);
+
+ void deleteClient(Client target);
+
+ void deleteProject(seedu.address.model.project.Project target);
/**
* Adds the given person.
* {@code person} must not already exist in the address book.
*/
- void addPerson(Person person);
+ void addDeveloper(Developer person);
+
+ void addClient(Client person);
+
+ void addProject(seedu.address.model.project.Project person);
/**
* Replaces the given person {@code target} with {@code editedPerson}.
* {@code target} must exist in the address book.
* The person identity of {@code editedPerson} must not be the same as another existing person in the address book.
*/
- void setPerson(Person target, Person editedPerson);
+ void setDeveloper(Developer target, Developer editedDeveloper);
+
+ void setClient(Client target, Client editedClient);
+
+ void setProject(seedu.address.model.project.Project target, seedu.address.model.project.Project editedProject);
+
+ /**
+ * Returns an unmodifiable view of the filtered person list
+ */
+ ObservableList getFilteredDeveloperList();
+
+ ObservableList getFilteredClientList();
+
+ ObservableList getFilteredProjectList();
- /** Returns an unmodifiable view of the filtered person list */
- ObservableList getFilteredPersonList();
/**
* Updates the filter of the filtered person list to filter by the given {@code predicate}.
+ *
* @throws NullPointerException if {@code predicate} is null.
*/
- void updateFilteredPersonList(Predicate predicate);
+ void updateFilteredDeveloperList(Predicate predicate);
+
+ void updateFilteredClientList(Predicate predicate);
+
+ void updateFilteredProjectList(Predicate predicate);
+
+ void updateFilteredProjectDeadlineList(Predicate predicate);
+
+ void commitAddressBook(Model model, String message, TabIndex index);
+
+ void undoAddressBook(Model model) throws CommandException;
+
+ void redoAddressBook(Model model) throws CommandException;
+
+ String getPreviousCommandForRedo() throws CommandException;
+ String getPreviousCommandForUndo() throws CommandException;
+
+ TabIndex getPreviousTabIndex();
+
+ TabIndex getPreviousTabIndexForRedo();
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..d8f882a5328 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -11,7 +11,12 @@
import javafx.collections.transformation.FilteredList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.TabIndex;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.client.Client;
+import seedu.address.model.developer.Developer;
import seedu.address.model.person.Person;
+import seedu.address.model.project.Deadline;
/**
* Represents the in-memory model of the address book data.
@@ -21,7 +26,10 @@ public class ModelManager implements Model {
private final AddressBook addressBook;
private final UserPrefs userPrefs;
- private final FilteredList filteredPersons;
+ private final FilteredList filteredDevelopers;
+ private final FilteredList