Skip to content

Commit

Permalink
Merge branch 'master' into add-new-class-diagrams
Browse files Browse the repository at this point in the history
  • Loading branch information
wangpeialex committed Nov 4, 2021
2 parents fac9749 + 76a34f0 commit aeaea6a
Show file tree
Hide file tree
Showing 98 changed files with 1,637 additions and 966 deletions.
142 changes: 67 additions & 75 deletions docs/DeveloperGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,104 +194,94 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa

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

### \[Proposed\] Undo/redo feature
### Ordering

#### Proposed Implementation
### Implementation

The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo
history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the
following operations:
When ModelManager is initialised, optionalOrder is set to Optional.empty().
At this point, the user has 1 order record with 2 items in his transaction list.

* `VersionedAddressBook#commit()` — Saves the current address book state in its history.
* `VersionedAddressBook#undo()` — Restores the previous address book state from its history.
* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history.
![Initial_State](images/OrderInitialState.png)

These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()`
and `Model#redoAddressBook()` respectively.
Step 1. The user enters ordering mode via the `sorder` command.

Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
Upon entering the ordering mode, optionalOrder now has a new Order() which is empty

Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the
initial address book state, and the `currentStatePointer` pointing to that single address book state.
![Sorder_State](images/OrderSorderState.png)

![UndoRedoState0](images/UndoRedoState0.png)
Step 2. The user adds an item to the order via the `iorder` command.

Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command
calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes
to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book
state.
Upon entering `iorder Banana c/1`, the order now contains 1 banana item.

![UndoRedoState1](images/UndoRedoState1.png)
![Iorder_State](images/OrderItem1State.png)

Step 3. The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitAddressBook()`
, causing another modified address book state to be saved into the `addressBookStateList`.
Next, upon entering `iorder Strawberry c/1`, the order now contains 1 strawberry item.

![UndoRedoState2](images/UndoRedoState2.png)
![Iorder_State](images/OrderItem2State.png)

<div markdown="span" class="alert alert-info">:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`.
Step 3. The user transacts the order via the `eorder` command.

</div>

Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing
the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer`
once to the left, pointing it to the previous address book state, and restores the address book to that state.

![UndoRedoState3](images/UndoRedoState3.png)

<div markdown="span" class="alert alert-info">:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather
than attempting to perform the undo.

</div>
After the transaction is done, optionalOrder is reinitialised to Optional.empty()

The following sequence diagram shows how the undo operation works:
![Initial_State](images/OrderFinalState.png)

![UndoSequenceDiagram](images/UndoSequenceDiagram.png)
Step 4. The new transactions are saved to json file.

<div markdown="span" class="alert alert-info">:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
![Transact_Order_Sequence_Diagram](images/TransactOrderSequenceDiagram.png)

</div>
### Mutating Inventory

The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once
to the right, pointing to the previously undone state, and restores the address book to that state.
This section explains how various commands update the list of items and display the result.

<div markdown="span" class="alert alert-info">:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
</div>
As a background context, all the item objects are contained in a `UniqueItemList` object which enforces uniqueness between
items and prevent duplicates. The `Inventory` manipulates the '`UniqueItemList` to update its content which then update the
`ObservableList<Item>`. The `ObservableList<Item>` is bounded to the UI so that the UI automatically updates when the
data in the list change.

Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such
as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`.
Thus, the `addressBookStateList` remains unchanged.
`UniqueItemList` is involved when the items are manipulated to ensure the uniqueness of the items. This, the design needs to
ensure that every command mutates the `UniqueItemList` through `Inventory`.

![UndoRedoState4](images/UndoRedoState4.png)
The general flow of inventory manipulation through AddCommand is as below:
1. The `AddCommand` object in `Logic` component interacts with `Model` component by calling the `Model#addItem()` if a
new item is added and `Model#restockItem()` if an existing item is restocked.
2. The `Model#addItem()` and `Model#restockItem()` methods then call methods with the same method signature in `Inventory`, `Inventory#addItem()` and `Inventory#restockItem()`.
3. The `Inventory` then manipulates the `UniqueItemList` by calling the methods with the same method signature, `UniqueItemList#addItem()` and `UniqueItemList#restockItem()`.
4. UniqueItemList then updates the `ObservableList#add` and `ObservableList#set` methods which updates the list to be returned to the user.
The returned list has added a new item or incremented the count of the existing item.

Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not
pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be
purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern
desktop applications follow.

![UndoRedoState5](images/UndoRedoState5.png)
Flow:`AddCommand` -> `Model` -> `Inventory` -> `UniqueItemList` -> `ObservableList<Item>`

The following activity diagram summarizes what happens when a user executes a new command:
The above flow applies for all the other similar commands that manipulates the inventory.
The detailed flow for each command is found below:

<img src="images/CommitActivityDiagram.png" width="250" />
**`AddCommand:`**
AddCommand#execute() -> Model#addItem() or Model#restockItem() -> Inventory#addItem() or Inventory#restockItem()
-> UniqueItemList#addItem() or UniqueItemList#setItem() -> ObservableList<Item>#add() or ObservableList<Item>#set()

#### Design considerations:
**`RemoveCommand:`**
RemoveCommand#execute() -> Model#removeItem() -> Inventory#removeItem() -> UniqueItemList#setItem() -> ObservableList<Item>#set()

**Aspect: How undo & redo executes:**
**`EditCommand:`**
EditCommand#execute() -> Model#setItem() -> Inventory#setItem() -> UniqueItemList#setItem() -> ObservableList<Item>#set()

* **Alternative 1 (current choice):** Saves the entire address book.
* Pros: Easy to implement.
* Cons: May have performance issues in terms of memory usage.
**`ClearCommand:`**
ClearCommand#execute() -> Model#setItem() -> Inventory#resetData() -> Inventory#setItems() -> UniqueItemList#setItem() -> ObservableList<Item>#set()

* **Alternative 2:** Individual command knows how to undo/redo by itself.
* Pros: Will use less memory (e.g. for `delete`, just save the person being deleted).
* Cons: We must ensure that the implementation of each individual command are correct.
**`DeleteCommand:`**
DeleteCommand#execute() -> Model#deleteItem() -> Inventory#deleteItems() -> UniqueItemList#removeItem() -> ObservableList<Item>#remove()

_{more aspects and alternatives to be added}_
**`SortCommand:`**
SortCommand#execute() -> Model#sortItem() -> Inventory#sortItems() -> UniqueItemList#sortItem() -> ObservableList<Item>#sort()

### \[Proposed\] Data archiving
#### Design considerations:

_{Explain here how the data archiving feature will be implemented}_
**Aspect:**

* **Finding Multiple Names, Ids or Tags:** The FindCommand supports finding by multiple names, ids or tags.
`IdContainsNumberPredicate`, `NameContainsKeywordsPredicate` and `TagContainsKeywordsPredicate` takes in a list of
strings which allows storing of multiple predicates. The items in the list are then matched with each predicate to
update the filtered list. Thus, the displayed list contains items that matches multiple predicates given.

--------------------------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -352,21 +342,23 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli

**Extensions**

* 1a. User did not specify the name of item.
* 1a1. BogoBogo notifies user of missing details.
* 1a. User is adding the item for the first time, and did not specify the id, cost price or sell price of the item.
* 1a1. BogoBogo informs user of the missing details.
* 1a2. User reenters with the missing details.

Use case ends.
Use case resumes at step 2.

* 1b. User is adding the item for the first time, and did not specify the id, price or cost of the item.
* 1b1. BogoBogo requests user for the missing details.
* 1b2. User enters the missing details.
* 1b. User is adding item that has been added before, and only specifies either name or id without the other fields.
* 1b1. BogoBogo will replenish the item according to the count indicated (count defaults to 1)

Use case resumes at step 2.
Use case ends.

* 1c. The given id does not match with the given name.
* 1c1. BogoBogo notifies user of the mismatch.
* 1c. User is adding an item that has been added before, but provides an id that corresponds to another item.
* 1c1. BogoBogo notifies user of the mismatch and shows the list of possible matches.
* 1c2. User reenters with the correct details.
* 1c3. BogoBogo will replenish the item according to the count indicated (count defaults to 1)

Use case resumes at step 2.
Use case ends.

**UC02 - Deleting an item**

Expand Down
10 changes: 7 additions & 3 deletions docs/UserGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,9 @@ Format:
Find items in the inventory.
Note that display must be in inventory mode (see `list`).

Format: `find [ n/{name}... | id/{id}... | t/{tag} ]`
Format: `find [ n/{name}... | id/{id}... | t/{tag}... ]`

Searching by 1 or more names, or 1 or more ids, or 1 or more tags is supported. However, searching by multiple tags simultaneously is not supported.
BogoBogo searches and lists items in the inventory that has matches any of the specified name, id, or tag.

Examples:

Expand Down Expand Up @@ -303,6 +303,10 @@ Format: `sorder`

Add an item into the current order. BogoBogo will let you know if there isn't enough items in the inventory to fulfill the order request.

<div class="code-example bg-grey-lt-000">
:bulb: Once having added items, you can view your current order with <code>list order</code>.
</div>

Format:
`iorder [ {name} | id/{id} ] (c/{count})`

Expand All @@ -324,7 +328,7 @@ Examples:
Cancels the specified order from the current order.

Format:
`corder [ {name} | id/{serial number} ]`
`corder [ {name} | id/{serial number} ] (c/COUNT)`

Flag | Argument | Description | Remarks |
--------|----------------|---------------------------------------------|-------------------------------------------------|
Expand Down
60 changes: 60 additions & 0 deletions docs/diagrams/FindSequenceDiagram.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
@startuml
!include style.puml

box Logic LOGIC_COLOR_T1
participant ":LogicManager" as LogicManager LOGIC_COLOR
participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
participant ":FindCommandParser" as FindCommandParser LOGIC_COLOR
participant ":FindCommand" as FindCommand LOGIC_COLOR
end box

box Model MODEL_COLOR_T1
participant ":Model" as Model MODEL_COLOR
participant ":NameContainsKeywordsPredicate" as NameContainsKeywordsPredicate MODEL_COLOR
end box
[-> LogicManager : execute(sort n/)
activate LogicManager


LogicManager -> AddressBookParser : parseCommand(sort n/)
activate AddressBookParser

create FindCommandParser
AddressBookParser -> FindCommandParser
deactivate FindCommand

create FindCommand
FindCommandParser -> FindCommand : parse(n/chocolate)
activate FindCommand

create NameContainsKeywordsPredicate
FindCommandParser -> NameContainsKeywordsPredicate : parse(n/chocolate)
activate NameContainsKeywordsPredicate

NameContainsKeywordsPredicate --> FindCommand
deactivate NameContainsKeywordsPredicate

FindCommand --> LogicManager
deactivate FindCommand

LogicManager -> FindCommand : execute()
activate FindCommand

FindCommand -> Model : updateFilteredItemList()
activate Model

Model -> NameContainsKeywordsPredicate : test()
activate NameContainsKeywordsPredicate

NameContainsKeywordsPredicate --> Model
deactivate NameContainsKeywordsPredicate

Model --> FindCommand
deactivate Model

FindCommand --> LogicManager : result
deactivate FindCommand

[<--LogicManager
deactivate LogicManager
@enduml
32 changes: 32 additions & 0 deletions docs/diagrams/OrderFinalState.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@startuml
!include style.puml
skinparam ClassFontColor #000000
skinparam ClassBorderColor #000000

title iorder state 2

Class modelManager as "__modelManager:ModelManager__"

Class optionalOrder as "__optionalOrder:OptionalOrder__"

Class trans2 as "__transactionRecord2:TransactionRecord__"
Class item1 as "__banana:Item__"
Class item2 as "__strawberry:Item__"
item2 -up-> trans2
item1 -up-> trans2

Class transactions as "__transactions:List<TransactionRecord>__"

Class trans1 as "__transactionRecord1:TransactionRecord__"
Class item1Trans1 as "__pencil:Item__"
Class item2Trans2 as "__pen:Item__"
item2Trans2 -up-> trans1
item1Trans1 -up-> trans1


transactions -up-> modelManager
trans1 -up-> transactions
trans2 -up-> transactions
optionalOrder -up-> modelManager

@end
25 changes: 25 additions & 0 deletions docs/diagrams/OrderInitialState.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@startuml
!include style.puml
skinparam ClassFontColor #000000
skinparam ClassBorderColor #000000

title Initial state

Class modelManager as "__modelManager:ModelManager__"

Class optionalOrder as "__optionalOrder:OptionalOrder__"

Class transactions as "__transactions:List<TransactionRecord>__"

Class trans1 as "__transactionRecord1:TransactionRecord__"
Class item1Trans1 as "__pencil:Item__"
Class item2Trans2 as "__pen:Item__"
item2Trans2 -up-> trans1
item1Trans1 -up-> trans1


transactions -up-> modelManager
optionalOrder -up-> modelManager
trans1 -up-> transactions

@end
28 changes: 28 additions & 0 deletions docs/diagrams/OrderItem1State.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@startuml
!include style.puml
skinparam ClassFontColor #000000
skinparam ClassBorderColor #000000

title iorder state 1

Class modelManager as "__modelManager:ModelManager__"

Class optionalOrder as "__optionalOrder:OptionalOrder__"
Class ord as "__order:Order__"
Class item1 as "__banana:Item__"

Class transactions as "__transactions:List<TransactionRecord>__"

Class trans1 as "__transactionRecord1:TransactionRecord__"
Class item1Trans1 as "__pencil:Item__"
Class item2Trans2 as "__pen:Item__"
item2Trans2 -up-> trans1
item1Trans1 -up-> trans1

transactions -up-> modelManager
trans1 -up-> transactions
item1 -up-> ord
ord -up-> optionalOrder
optionalOrder -up-> modelManager

@end
Loading

0 comments on commit aeaea6a

Please sign in to comment.