Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add implementation text for person commands #322

104 changes: 102 additions & 2 deletions docs/DeveloperGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ The bulk of the app's work is done by the following four components:

**How the architecture components interact with each other**

The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 0000`, assuming that `0000` corresponds to a valid Person UUID.
The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete /0001`, assuming that `0001` corresponds to a valid Person UUID.

<img src="images/ArchitectureSequenceDiagram.png" width="574" />
<img src="images/DeletePersonSequenceDiagram.png" width="574" />

Each of the four main components (also shown in the diagram above),

Expand Down Expand Up @@ -272,6 +272,106 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa

This section describes some noteworthy details on how certain features are implemented.

### Add Person feature

This feature is the `Add` command that allows users to add a person to the address book.
This feature creates a person with 0 or more attributes, and then adds that person to the address book.

#### Implementation

Adding a person is carried out using `AddCommand` and `AddCommandParser`.
The `AddCommand` class extends the `Command` class.
The `AddCommandParser` class extends the `Parser` class.

Given below is an example usage scenario and how adding a person works.

Step 1: The user enters a command to add a person with `Name` attribute to their family tree in Gene-nie.
For example: `add /Name Bob`.

Step 2: The `LogicManager` component receives this command as a string and passes it to the `AddressBookParser`.

Step 3: The `AddressBookParser` recognizes the `add` keyword and creates a new `AddCommandParser`.

Step 4: The `AddCommandParser` parses the rest of the command `/Name Bob`.
It creates a `HashMap` containing the attribute `Name` with value `Bob`.
It then creates a new `AddCommand` with this `HashMap`.
In this step, duplicate attribute names are also checked for, as part of the HashMap creation process in ParserUtil.

Step 5: The `AddCommand` is executed.

Step 6: `AddCommand#execute` continues by calling the following method from `Model`:
* `Model#addPerson(Person)` It adds a given person to the address book.

Step 7: The `AddCommand#execute` method returns the `CommandResult` object to the `LogicManager` component.

The following sequence diagram shows how adding a person works:

![AddPersonSequenceDiagram](images/AddPersonSequenceDiagram.png)

<div markdown="span" class="alert alert-info">:information_source: **Note:** The lifelines should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

The following activity diagram sheds more light on exactly what happens when a user executes the 'add' command:

![AddPersonActivityDiagram](images/AddPersonActivityDiagram.png)

#### Design Considerations

##### Aspect: When to check for duplicate attributes
* Alternative 1 (current choice): `AddCommand#execute` checks for duplicates before adding the person to the addressbook.
* Pros: Makes the command more atomic - if there is an error, no changes are made.
* Cons: Less efficient as the program iterates through all attributes twice.
* Alternative 2: `AddCommand#execute` adds an empty person to the address book and then checks for duplicates while appending attributes to that person.
* Pros: Shorter code, less repetition of actions.
* Cons: Duplicate attributes would halt command and leave the address book in a partially edited state. Fixing this would result in less readable code.

[Back to Table of Contents](#table-of-contents)

--------------------------------------------------------------------------------------------------------------------
### Delete Person feature

This feature is the `Delete` command that allows users to remove a person from the address book.

#### Implementation

Deleting a person is carried out using `DeleteCommand` and `DeleteCommandParser`.
The `DeleteCommand` class extends the `Command` class.
The `DeleteCommandParser` class extends the `Parser` class.

Given below is an example usage scenario and how deleting a person works.

Step 1: The user enters a command to delete a person with the UUID `0001`.
For example: `delete /0001`.

Step 2: The `LogicManager` component receives this command as a string and passes it to the `AddressBookParser`.

Step 3: The `AddressBookParser` recognizes the `delete` keyword and creates a new `DeleteCommandParser`.

Step 4: The `DeleteCommandParser` parses the rest of the command `/0001`.
It then creates a new `DeleteCommand` with the UUID `0001`.

Step 5: The `DeleteCommand` is executed.

Step 6: `DeleteCommand#execute` continues by calling the following method from `Model`:
* `Model#getFullUuid(String)` It takes in the partial 4-character UUID provided by the user and converts it to the `UUID` object representative of the person to delete.
* `Model#getPersonByUuid(UUID)` It takes the full UUID obtained earlier and returns the corresponding `Person` object.
* `Model#deletePerson(Person)` It deletes the provided person from the address book.
* `Model#deleteRelationshipsOfPerson(UUID)` It deletes all relationships associated with the person being deleted.

Step 7: The `DeleteCommand#execute` method returns the `CommandResult` object to the `LogicManager` component.

The following sequence diagram shows how deleting a person works:

![DeletePersonSequenceDiagram](images/DeletePersonSequenceDiagram.png)

<div markdown="span" class="alert alert-info">:information_source: **Note:** The lifelines should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

The following activity diagram sheds more light on exactly what happens when a user executes the 'delete' command:

![DeletePersonActivityDiagram](images/DeletePersonActivityDiagram.png)

[Back to Table of Contents](#table-of-contents)

--------------------------------------------------------------------------------------------------------------------
### Add Attribute feature

An `AddAttribute` feature that allows users to add attributes to a person in the address book. This feature ensures that only unique attributes are added to a person, maintaining data integrity.
Expand Down
24 changes: 24 additions & 0 deletions docs/diagrams/AddPersonActivityDiagram.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@startuml
skin rose
skinparam ActivityFontSize 15
skinparam ArrowFontSize 12
skinparam ArrowFontStyle plain

start

:User enters command;

:Parse command for attributes of person to add;

if (Are there duplicate attribute names?) then (no)
:Create a new person;
:Add the attributes to the person;
:Add the person to the model;
:Save the address book containing the new person to storage;
:Inform user: New person added. Details: ...;
else (yes)
:Inform user: Duplicate attributes found ...;
endif

stop
@enduml
57 changes: 57 additions & 0 deletions docs/diagrams/AddPersonSequenceDiagram.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@startuml
!include style.puml
skinparam ArrowFontStyle plain

Actor User as user USER_COLOR
Participant ":UI" as ui UI_COLOR
Participant ":Logic" as logic LOGIC_COLOR
Participant ":Model" as model MODEL_COLOR
Participant ":Person" as person PERSON_COLOR
Participant ":Attribute" as attribute ATTRIBUTE_COLOR
Participant ":Storage" as storage STORAGE_COLOR

user -[USER_COLOR]> ui : "add /Name Bob"
activate ui UI_COLOR

ui -[UI_COLOR]> logic : execute("add /Name Bob")
activate logic LOGIC_COLOR

loop all attributes in command
logic -[LOGIC_COLOR]> attribute : AttributeUtil.createAttribute(attributeName, attributeValue)
activate attribute ATTRIBUTE_COLOR

attribute --[ATTRIBUTE_COLOR]> logic : return new Attribute
deactivate attribute
end

logic -[LOGIC_COLOR]> person : Person(attributesToAdd)
activate person PERSON_COLOR

person --[PERSON_COLOR]> logic : return new Person
deactivate person
destroy person

logic -[LOGIC_COLOR]> model : addPerson(personToAdd)
activate model MODEL_COLOR

model --[MODEL_COLOR]> logic : return added Person
deactivate model

logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook)
activate storage STORAGE_COLOR

storage -[STORAGE_COLOR]> storage : Save to file
activate storage STORAGE_COLOR_T1
storage --[STORAGE_COLOR]> storage : file saved
deactivate storage STORAGE_COLOR_T1

storage --[STORAGE_COLOR]> logic : save complete
deactivate storage
destroy storage

logic --[LOGIC_COLOR]> ui : CommandResult("New person added. Details: Name: Bob")
deactivate logic

ui--[UI_COLOR]> user : display "New person added. Details: Name: Bob"
deactivate ui
@enduml
25 changes: 25 additions & 0 deletions docs/diagrams/DeletePersonActivityDiagram.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@startuml
skin rose
skinparam ActivityFontSize 15
skinparam ArrowFontSize 12
skinparam ArrowFontStyle plain

start

:User enters command;

:Parse command for 4-character UUID;
:Get full UUID;

if (Does full UUID exist?) then (yes)
:Get the corresponding person from address book;
:Delete person from address book;
:Delete person's relationships from address book;
:Save updated address book with deleted person to storage;
:Inform user: Deleted Person: Details: ...;
else (no)
:Inform user: The UUID provided is invalid...;
endif

stop
@enduml
49 changes: 49 additions & 0 deletions docs/diagrams/DeletePersonSequenceDiagram.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@startuml
!include style.puml
skinparam ArrowFontStyle plain

Actor User as user USER_COLOR
Participant ":UI" as ui UI_COLOR
Participant ":Logic" as logic LOGIC_COLOR
Participant ":Model" as model MODEL_COLOR
Participant ":Storage" as storage STORAGE_COLOR

user -[USER_COLOR]> ui : "delete /0001"
activate ui UI_COLOR

ui -[UI_COLOR]> logic : execute("delete /0001")
activate logic LOGIC_COLOR

logic -[LOGIC_COLOR]> model : getFullUuid("0001")
activate model MODEL_COLOR

model --[MODEL_COLOR]> logic : return UUID object
deactivate model

logic -[LOGIC_COLOR]> model : person.getPersonbyUuid(targetUuid)
activate model MODEL_COLOR

model --[MODEL_COLOR]> logic : return matching Person
deactivate model

logic -[LOGIC_COLOR]> model : deletePerson(personToDelete)
logic -[LOGIC_COLOR]> model : deleteRelationshipsOfPerson(targetUuid)

logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook)
activate storage STORAGE_COLOR

storage -[STORAGE_COLOR]> storage : Save to file
activate storage STORAGE_COLOR_T1
storage --[STORAGE_COLOR]> storage : file saved
deactivate storage STORAGE_COLOR_T1

storage --[STORAGE_COLOR]> logic : save complete
deactivate storage
destroy storage

logic --[LOGIC_COLOR]> ui : CommandResult("Deleted Person: Details: ...")
deactivate logic

ui--[UI_COLOR]> user : display "Deleted Person: Details: ..."
deactivate ui
@enduml
Binary file added docs/images/AddPersonActivityDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/AddPersonSequenceDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/DeletePersonActivityDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/DeletePersonSequenceDiagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 0 additions & 7 deletions src/main/java/seedu/address/logic/commands/AddCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,6 @@ public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);

Attribute[] attributesToAdd = generateAttributeList();
for (int i = 0; i < attributesToAdd.length; i++) {
for (int j = i; j < attributesToAdd.length; j++) {
if (i != j && attributesToAdd[i].getName().equalsIgnoreCase(attributesToAdd[j].getName())) {
throw new CommandException(Messages.MESSAGE_DUPLICATE_ATTRIBUTES);
}
}
}
Person addedPerson = addPersonToModel(model, attributesToAdd);

return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(addedPerson)));
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/seedu/address/logic/parser/ParserUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,11 @@ public static HashMap<String, String> getAttributeHashMapFromAttributeStrings(St
HashMap<String, String> attributeMap = new HashMap<>();
for (int i = 0; i < parts.length; i++) {
String[] attribute = separateAttributeNamesAndValues(parts[i]);
String attributeName = attribute[0];
String attributeName = attribute[0].toLowerCase();
String attributeValue = attribute[1];
if (attributeMap.containsKey(attributeName.toLowerCase())) {
throw new ParseException(Messages.MESSAGE_DUPLICATE_ATTRIBUTES);
}
attributeMap.put(attributeName, attributeValue);
}
return attributeMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,6 @@ public void toStringMethod() {
assertEquals(expected, addCommand.toString());
}

@Test
public void multipleSameAttributes() {
HashMap<String, String> aliceHashMap = new HashMap<>();
aliceHashMap.put("Name", "Alice");
aliceHashMap.put("name", "Alice");
AddCommand addCommand = new AddCommand(aliceHashMap);
assertThrows(CommandException.class, () -> addCommand.execute(model));
}

/**
* A default model stub that have all of the methods failing.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class AddCommandParserTest {
@Test
public void parse_singleAttribute_success() {
HashMap<String, String> expectedAttributeMap = new HashMap<>();
expectedAttributeMap.put("Name", VALID_NAME_BOB);
expectedAttributeMap.put("name", VALID_NAME_BOB);

// whitespace only preamble
assertParseSuccess(parser, "add /Name " + VALID_NAME_BOB, new AddCommand(expectedAttributeMap));
Expand All @@ -33,10 +33,10 @@ public void parse_singleAttribute_success() {
@Test
public void parse_multipleAttributes_success() {
HashMap<String, String> expectedAttributeMap = new HashMap<>();
expectedAttributeMap.put("Name", VALID_NAME_BOB);
expectedAttributeMap.put("Phone", VALID_PHONE_BOB);
expectedAttributeMap.put("Email", VALID_EMAIL_BOB);
expectedAttributeMap.put("Address", VALID_ADDRESS_BOB);
expectedAttributeMap.put("name", VALID_NAME_BOB);
expectedAttributeMap.put("phone", VALID_PHONE_BOB);
expectedAttributeMap.put("email", VALID_EMAIL_BOB);
expectedAttributeMap.put("address", VALID_ADDRESS_BOB);


assertParseSuccess(parser, "add /Name " + VALID_NAME_BOB + " /Phone " + VALID_PHONE_BOB + " /Email "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ public class AddressBookParserTest {
@Test
public void parseCommand_add() throws Exception {
Person person = new Person(new Attribute[0]);
person.updateAttribute(new NameAttribute("Name", "Amy Bee"));
person.updateAttribute(new NameAttribute("name", "Amy Bee"));
AddCommand command = (AddCommand) parser.parseCommand(
AddCommand.COMMAND_WORD
+ " /Name Amy Bee");
HashMap<String, String> attributes = new HashMap<>();
attributes.put("Name", "Amy Bee");
attributes.put("name", "Amy Bee");
assertEquals(new AddCommand(attributes), command);
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/java/seedu/address/logic/parser/ParserUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ public void getAttributeHashMapFromAttributeStrings_emptyArray_emptyHashMap() {
public void getAttributeHashMapFromAttributeStrings_validArray_validHashMap() {
String[] parts = {"Name Rachel", " Phone 123456 "};
try {
assertEquals("Rachel", ParserUtil.getAttributeHashMapFromAttributeStrings(parts).get("Name"));
assertEquals("123456", ParserUtil.getAttributeHashMapFromAttributeStrings(parts).get("Phone"));
assertEquals("Rachel", ParserUtil.getAttributeHashMapFromAttributeStrings(parts).get("name"));
assertEquals("123456", ParserUtil.getAttributeHashMapFromAttributeStrings(parts).get("phone"));
} catch (ParseException e) {
e.printStackTrace();
}
Expand Down
Loading