Developer Guide
- Acknowledgements
- Setting up, getting started
- Design
- Implementation
- Documentation, logging, testing, configuration, dev-ops
- Appendix: Requirements
- Appendix: Instructions for manual testing
Acknowledgements
- Creating a pop-up menu in JavaFX
- Removing a JavaFX row in run-time
- Getting the row index constraint for given node
- Using new DateTimeFormatter to do strict date parsing
Setting up, getting started
Refer to the guide Setting up and getting started.
Design

.puml
files used to create diagrams in this document can be found in the diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.
Architecture
The architecture diagram given above explains the high-level design of the App.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
Main
has two classes called Main
and MainApp
. It is responsible for,
- At App launch: Initializes the components in the correct sequence, and connects them up with each other.
- At shut down: Shuts down the components and invokes cleanup methods where necessary.
Commons
represents a collection of classes used by multiple other components.
The rest of the App consists of four components.
-
UI
: The UI of the App. -
Logic
: The command executor. -
Model
: Holds the data of the App in memory. -
Storage
: Reads data from, and writes data to, the hard disk.
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 sdel 1
.
Each of the four main components (also shown in the diagram above),
- defines its API in an
interface
with the same name as the Component. - implements its functionality using a concrete
{Component Name}Manager
class (which follows the corresponding APIinterface
mentioned in the previous point.
For example, the Logic
component defines its API in the Logic.java
interface and implements its functionality using the LogicManager.java
class which follows the Logic
interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component’s being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
The sections below give more details of each component.
UI component
The API of this component is specified in Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, StallListPanel
, ReviewListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class which captures the commonalities between classes that represent parts of the visible GUI.
The UI
component uses the JavaFX UI framework. The layout of these UI parts are defined in matching .fxml
files that are in the src/main/resources/view
folder. For example, the layout of the MainWindow
is specified in MainWindow.fxml
The UI
component,
- executes user commands using the
Logic
component. - listens for changes to
Model
data so that the UI can be updated with the modified data. - keeps a reference to the
Logic
component, because theUI
relies on theLogic
to execute commands. - depends on some classes in the
Model
component, as it displaysStall
object residing in theModel
.
Logic component
API : Logic.java
Here’s a (partial) class diagram of the Logic
component:
How the Logic
component works:
- When
Logic
is called upon to execute a command, it uses theAddressBookParser
class to parse the user command. - This results in a
Command
object (more precisely, an object of one of its subclasses e.g.,RAddCommand
) which is executed by theLogicManager
. - The command can communicate with the
Model
when it is executed (e.g. to add a review). - The result of the command execution is encapsulated as a
CommandResult
object which is returned back fromLogic
.
The sequence diagram below illustrates the interactions within the Logic
component for the execute("rdel 1")
API call.

RDeleteCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Here are the other classes in Logic
(omitted from the class diagram above) that are used for parsing a user command:
How the parsing works:
- When called upon to parse a user command, the
AddressBookParser
class creates anXYZCommandParser
(XYZ
is a placeholder for the specific command name e.g.,RAddCommandParser
) which uses the other classes shown above to parse the user command and create aXYZCommand
object (e.g.,RAddCommand
) which theAddressBookParser
returns back as aCommand
object. - All
XYZCommandParser
classes (e.g.,RAddCommandParser
,RDeleteCommandParser
, …) inherit from theParser
interface so that they can be treated similarly where possible e.g, during testing.
Model component
API : Model.java
The Model
component,
- stores the address book data i.e., all
Stall
objects (which are contained in aUniqueStallList
object) and allReview
objects (which are contained in aUniqueReviewList
object). - stores the currently ‘selected’
Stall
andReview
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<Stall>
andObservableList<Review>
that can be ‘observed’ e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. - stores a
UserPref
object that represents the user’s preferences. This is exposed to the outside as aReadOnlyUserPref
objects. - maintains its list of
Review
objects to reflect the reviews stored in theStall
objects it stores after each operation. - does not depend on any of the other three components (as the
Model
represents data entities of the domain, they should make sense on their own without depending on other components)

Tag
list in the AddressBook
, which Stall
references. This allows AddressBook
to only require one Tag
object per unique tag, instead of each Stall
needing their own Tag
objects.
Storage component
API : Storage.java
The Storage
component,
- can save both address book data and user preference data in JSON format, and read them back into corresponding objects.
- inherits from both
AddressBookStorage
andUserPrefStorage
, which means it can be treated as either one (if only the functionality of only one is needed). - stores
Review
objects withinStall
objects for address book data. - depends on some classes in the
Model
component (because theStorage
component’s job is to save/retrieve objects that belong to theModel
)
Common classes
Classes used by multiple components are in the foodwhere.commons
package.
Implementation
This section describes some noteworthy details on how certain features are implemented.
Review components
- Added classes into the
Model
component to encapsulate a Review
Implementation
A Review
,
- is primarily stored in the Stall associated with it
- is also stored in the
uniqueReviewList
of the Model (reflecting the reviews as stored in the Stall objects after each operation)
A Review
contains the following attributes,
- a
Name
, which represent the name of the Stall associated with the Review - an
Address
, which represent the address of the Stall associated with the Review - a
Date
, which represent the day, month and year as specified inDD/MM/YYYY
, or any of the specified formats in here - a
Content
, which represent the review of the Stall by the user - a
Rating
, which represent the rating of the Stall from 0 to 5 inclusive - can be assigned to a single
Stall
- can be assigned multiple
Tag
s
Design considerations:
Aspect: How the components within Review are added or changed
-
Current Choice: Attributes within
Review
are immutable, meaning that if there is an attribute that has to be edited, a newReview
object has to be created.- Pros: Concept of Immutability is met, making the code less prone to bugs as all components of a
Review
object are fixed - Cons: Less flexible, more steps needed in editing
Review
objects
- Pros: Concept of Immutability is met, making the code less prone to bugs as all components of a
- Alternative 1: Allow certain components within
Review
, likeDate
andContent
to be mutable- Pros: Less overhead as fewer objects are created
- Cons: Prone to error as a Component might not be correctly changed
Review adding feature
What is Review adding feature about?
The add review mechanism is facilitated by AddressBook
. This feature enhances AddressBook
by allowing to store not only Stall
, but also Review
. This is stored internally as a UniqueStallList
and UniqueReviewList
. Review
requires a Stall
as Review
is stored in Stall
. Additionally, the feature implements the following operations:
-
AddressBook#addReview(Review)
— Adds theReview
toUniqueReviewList
For the command, the feature extends Command
, and is implemented as such:
radd s/STALL_INDEX d/DATE c/CONTENT r/RATING [t/TAGS]…
Implementation flow of Review adding feature
Given below is an example usage scenario and how the Review adding mechanism behaves at each step.
Note: FoodWhere comes with preloaded data, and can be started on a fresh state with the clear
command.
Step 1. The user launches the application for the first time. FoodWhere will be initialized with the preloaded data. Ensure that there is an existing Stall
with index 1.
Step 2. The user executes radd s/1 d/20-09-2022 c/The food was good, the chicken rice was fresh. r/4
command to create a new Review
for Stall
with index 1.
UML diagram for adding Review
The following activity diagram summarizes what happens when a user executes a new radd
command:
Design considerations:
- The Review adding commands are straight to the point and efficient for users to add Review for Stall in FoodWhere.
- The prefixes allow users to understand what the different types of data fields Review need in order to be created.
Finding Stalls and Reviews feature
What is finding Stall and Review feature about?
This feature is used to find stalls and reviews in FoodWhere by name and/or by tags. It uses the following two commands:
-
sfind
: Finds stalls -
rfind
: Finds reviews
sfind
allows users to find stalls in AddressBook
by name, through matching of input keyword(s) with stall names. Additionally, users can find stalls by tags, through matching of input keyword(s) with stall tags.
rfind
allows users to find reviews in AddressBook
by name, through matching of input keyword(s) with review names. Additionally, users can find reviews by tags, through matching of input keyword(s) with review tags.
For the command, the feature extends Command
, and is implemented as such:
sfind n/[NAME_KEYWORDS]… t/[TAG_KEYWORDS]…
rfind n/[NAME_KEYWORDS]… t/[TAG_KEYWORDS]…
Implementation flow of finding Stalls and Reviews feature
Given below is an example usage scenario and how the finding stalls and reviews mechanism behaves at each step.
Step 1. The user executes sfind n/eatery
command to find all stalls where stall name contains the word ‘eatery’.
Step 2. The user input will be sent to SFindCommandParser
.
Step 3. The keyword eatery
will be parsed as a Name.
Step 4. The parser creates a StallContainsKeywordsPredicate
using the Name created, while setting the tag attribute to null.
Step 5. The predicate is used to create a new SFindCommand
.
Step 6. When the SFindCommand executes, the predicate will be sent to ModelManager to filter out stalls that satisfy the predicate.
UML diagram for finding Stalls/Reviews
The following activity diagram summarizes what happens when a user executes a new sfind
or rfind
command:
Design considerations:
- Allow usage of multiple attributes (name and/or tag) as search term to filter out stalls/reviews that have the specified keywords
- Users can enter multiple search keywords to find all relevant stalls/reviews
Listing all Reviews feature
What is listing all Reviews feature about?
The listing of all reviews mechanism is facilitated by Model
. This feature allows the user to list all reviews.
For the command, the feature extends Command
, and is implemented as such:
rlist
Implementation flow of listing all Reviews feature
Given below is an example usage scenario and how the listing of all reviews mechanism behaves at each step.
Note: FoodWhere comes with preloaded data, and can be started on a fresh state with the clear
command.
Step 1. The user launches the application for the first time. FoodWhere will be initialized with the preloaded data.
Step 2. The user executes rlist
to list all reviews on the FoodWhere user interface.
UML diagram for listing of all Reviews
The following activity diagram summarizes what happens when a user executes a new rlist
command:
Review deleting feature
What is Review deleting feature about?
The delete review mechanism is facilitated by AddressBook
. This feature allows the user to delete a review.
For the command, the feature extends Command
, and is implemented as such:
rdel REVIEW_INDEX
Implementation flow of Review deleting feature
Given below is an example usage scenario and how the listing of all reviews mechanism behaves at each step.
Note: FoodWhere comes with preloaded data, and can be started on a fresh state with the clear
command.
Step 1. The user launches the application for the first time. FoodWhere will be initialized with the preloaded data.
Step 2. The user list all the reviews by entering the rlist
command. Ensure that there is an existing Review with index 2 in the review list.
Step 3. The user executes rdel 2
command to delete the last review with index 2.
UML diagram for deleting Review
The following activity diagram summarizes what happens when a user executes a new rdel
command:
Review editing feature
What is Review editing feature about?
The edit review mechanism is facilitated by REditCommandParser
and REditCommand
. This feature allows the user to edit a review after it has been created.
REditCommandParser.parse()
- parses the user input and returns a REditCommand
object. REditCommand.execute()
- creates a new Review
object based on the parsed user input and calls Model.setReview()
to replace the old Review
object with the new Review
object.
For the command, the feature extends Command
, and is implemented as such:
redit INDEX [d/DATE] [c/CONTENT] [r/RATING] [t/TAGS]…
Implementation flow of Review editing feature
Given below is an example usage scenario and how the listing of all reviews mechanism behaves at each step.
Note: FoodWhere comes with preloaded data, and can be started on a fresh state with the clear
command.
Step 1. The user launches the application for the first time. FoodWhere will be initialized with the preloaded data. Ensure that there is an existing Review with index 2 in the review list.
Step 2. The user executes redit 2 r/5
command to edit Review
with index 2 to edit its rating to 5.
Step 3. Since the user input is valid, the AddressBookParser
will create a REditCommandParser
to parse the command arguments, 2 r/5
.
Step 4. REditCommandParser
will parse the index to a Index
object and parse other arguments as a EditReviewDescriptor
object. The Index
and EditReviewDescriptor
objects will then be passed to the returned REditCommand
object as its arguments.
Step 5. In LogicManager
, the returned REditCommand
is executed.
Step 6. In the execution of the REditCommand
, a new Review
object is created. This Review
consists of the field(s) from the parsed user input, replacing some field(s) of the original Review
object. In this case, the original Review
with index 2 is copied over to a new Review
object except its Rating
field which is set as 5
.
Step 7. model.setReview()
will interact with the model to have it replace the immutable Stall
which contained the original Review
with a Stall
containing the updated Review
.
UML diagram for editing Review
The following activity diagram summarizes what happens when a user executes a new redit
command:
Design considerations:
- Multiple fields of a Review can be edited in one go to increase the efficiency of the user of our application.
Sorting Stalls and Reviews feature
What is sorting Stalls and Reviews feature about?
ssort
: The sort stalls mechanism is facilitated by Model
and StallsComparatorList
. This feature allows the user to sort all stalls by specified criterion. The list of supported sorting criteria is stored in StallsComparatorList
enum class as enum constants. Each enum constant has a Comparator<Stall>
field that will be passed in as an argument for Model.sortStalls()
for sorting the stall list.
rsort
: The sort reviews mechanism is facilitated by Model
and ReviewsComparatorList
. This feature allows the user to sort all reviews by specified criterion. The list of supported sorting criteria is stored in ReviewsComparatorList
enum class as enum constants. Each enum constant has a Comparator<Review>
field that will be passed in as an argument for Model.sortReviews()
for sorting the review list.
For the command, the feature extends Command
, and is implemented as such:
ssort CRITERION
rsort CRITERION
Implementation flow of sorting Stalls and Reviews feature
Given below is an example usage scenario and how the sorting of all stalls and reviews mechanism behaves at each step.
Note: FoodWhere comes with preloaded data, and can be started on a fresh state with the clear
command.
Step 1. The user launches the application for the first time. FoodWhere will be initialized with the preloaded data.
Step 2. The user executes rsort reversedname
command to sort Review
by name (from Z to A, followed by 9 to 0).
Step 3. Since the user input is valid, the AddressBookParser
will create a RSortCommandParser
to parse the command arguments, reversedname
.
Step 4. RSortCommandParser
will parse the criterion to a ReviewsComparatorList
object. The ReviewsComparatorList
object will then be passed to the returned RSortCommand
object as its argument.
Step 5. In LogicManager
, the returned RSortCommand
is executed.
Step 6. model.sortReviews()
will interact with the model to sort reviews using the custom comparator that is retrieved from ReviewsComparatorList.getComparator()
.
UML diagram for sorting Stalls/Reviews
The following activity diagram summarizes what happens when a user executes a new ssort
or rsort
command:
File format for FoodWhere
FoodWhere data is stored as a JSON file.
- The file stores the stalls in the
"stalls"
property of the object - Each of the stalls is represented by an object with the properties
"name"
,"address"
,"tags"
,"reviews"
. - The reviews for a stall is stored in the
"reviews"
property of the object representing the stall. - Each review is represented by an object with the properties
"date"
,"content"
,"rating"
,"tags"
.
Aside from rating, the other fields are stored as strings. The accepted format includes the following:
Data field | Format |
---|---|
Address | Any nonempty ASCII text |
Content | Any nonempty ASCII text |
Date | A date in the format DD/MM/YYYY, D/MM/YYYY, DD/M/YYYY, D/M/YYYY, or with dashes instead of slashes |
Name | Nonempty alphanumeric string with spaces, capitalization preserved, duplicate spaces removed for the actual name |
Rating | An integer or floating point number from 0 (inclusive) to 6 (exclusive), rounded down (after interpreted as a floating point number) for the actual rating |
Tag | Alphanumeric token without spaces, interpreted as lowercase for the actual tag |
Below is an example of one stall with one review.
{
"stalls" : [ {
"name" : "Alex Chicken Rice",
"address" : "Blk 30 Geylang Street 29, #06-40",
"tags" : [ "chickenrice" ],
"reviews" : [ {
"date" : "20/09/2022",
"content" : "Very tasty. Worth the trip",
"rating" : 5,
"tags" : [ "travelworthy" ]
} ]
} ]
}
[Proposed] Undo/redo feature
Proposed 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:
-
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.
These operations are exposed in the Model
interface as Model#commitAddressBook()
, Model#undoAddressBook()
and Model#redoAddressBook()
respectively.
Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
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.
Step 2. The user executes sdel 5
command to delete the 5th stall in the address book. The sdel
command calls Model#commitAddressBook()
, causing the modified state of the address book after the sdel 5
command executes to be saved in the addressBookStateList
, and the currentStatePointer
is shifted to the newly inserted address book state.
Step 3. The user executes sadd n/Davids Delights …
to add a new stall. The sadd
command also calls Model#commitAddressBook()
, causing another modified address book state to be saved into the addressBookStateList
.

Model#commitAddressBook()
, so the address book state will not be saved into the addressBookStateList
.
Step 4. The user now decides that adding the stall 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.

currentStatePointer
is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The undo
command uses Model#canUndoAddressBook()
to check if this is the case. If so, it will return an error to the user rather
than attempting to perform the undo.
The following sequence diagram shows how the undo operation works:

UndoCommand
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
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.

currentStatePointer
is at index addressBookStateList.size() - 1
, pointing to the latest address book state, then there are no undone AddressBook states to restore. The redo
command uses Model#canRedoAddressBook()
to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
Step 5. The user then decides to execute the command slist
. Commands that do not modify the address book, such as slist
, will usually not call Model#commitAddressBook()
, Model#undoAddressBook()
or Model#redoAddressBook()
. Thus, the addressBookStateList
remains unchanged.
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 sadd n/Davids Delights …
command. This is the behavior that most modern desktop applications follow.
The following activity diagram summarizes what happens when a user executes a new command:
Design considerations:
Aspect: How undo & redo executes:
-
Alternative 1: Saves the entire address book.
- Pros: Easy to implement.
- Cons: May have performance issues in terms of memory usage.
-
Alternative 2: Individual command knows how to undo/redo by
itself.
- Pros: Will use less memory (e.g. for
sdel
, just save the stall being deleted). - Cons: We must ensure that the implementation of each individual command are correct.
- Pros: Will use less memory (e.g. for
-
Alternative 3 (current choice): Saves the entire address book, but ensuring that Stall and Review are both immutable.
- Pros: Easy to implement, performance issues for memory are not too bad.
- Cons: This needs Stall and Review to be guaranteed immutable, alongside all their parts. Good test cases are a must to avoid regressions making them mutable.
Documentation, logging, testing, configuration, dev-ops
Appendix: Requirements
Product scope
Target user profile:
- Food critics who want to keep track of all the food stalls and reviews given
- have a need to manage a significant number of food stalls and reviews
- prefer desktop apps over other types
- can type fast
- prefer typing to mouse interactions
- is reasonably comfortable using CLI apps
- need a local database to store all information
Value proposition:
- An application to show all the food stalls and reviews given to the particular stalls
- Commands are typed using command line
- Manage stalls faster than a typical mouse/GUI driven app
- All stalls/reviews created are stored in the local database
User stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… | Implemented in current version |
---|---|---|---|---|
* * * |
user | create reviews for a food stall | record which food stall that I have visited have nice food | Yes |
* * * |
user | view reviews for a food stall | easily find out the best food I have eaten | Yes |
* * * |
user | delete review for food stall | delete any erroneous entries I have made | Yes |
* * |
user | modify details of review for food stall | rectify any erroneous details in the entry | Yes |
* * * |
user | list out food stall | have a overview of the food stalls I have been to | Yes |
* * |
user | list out food stall according from high to low reviews | see the top few food stall | No |
* * * |
user | find food stall by matching a word in the name | find the exact food stall I am interested in | Yes |
* |
user | find food stall by approximate name | find the exact food stall I am interested in even when I’m not very sure about the stall name | No |
* |
user | tag a food stall with a tag | categorize food stalls effectively | Yes |
* |
user | list out food stalls that contain a given tag | find all food stalls that contain the tag I am interested in | Yes |
* |
user | include photo along with the review | easily identify which photo belongs to which stall and upload them to social media | No |
* * * |
user | purge existing data | get rid of any sample data | Yes |
* * * |
user | add food stall addresses | add a new location I can review | Yes (by adding a Stall) |
* * * |
user | delete food stall addresses | remove a stall when it closes | Yes |
* * * |
new user | see the application populated with sample data | view what the app should look like | Yes |
* * |
user | modify the address of a food stall | update when a hawker moves to a different place | Yes |
* |
user | find places close to my current location | choose where to go next effectively | No |
* |
user | store individual food ratings of a place | see which food I have reviewed from a place | Yes |
* |
user | search food places by food type | organize my work to sure variety in food type in my reviews (for example, same/different types of food in the same review or to facilitate comparison) | Yes (by Tag) |
* |
user | send the reviews through other social media platforms | share the review with my friends without having to make separate posts | No |
* * * |
user | include a date/day on my reviews | track how much reviewing I have done over time | Yes (by Date) |
* |
user | see review that are most recent by date (sorting) | get the most updated review | Yes |
* |
user | archive existing stalls/reviews | not be distracted by previous reviews made | Yes (by copying data file to another location) |
* * * |
new user | check out what tools are available in this application | learn how to use the application | Yes |
* * * |
user helping another user | import data | get existing lists from friends/coworkers to work on | Yes (by copying data into foodwhere.json file) |
* * * |
user | export data | archive my data entries somewhere else | Yes (by copying data file to another location) |
* |
user | set a deadline to review a particular stall | remind myself to complete the task | No |
* |
experienced user | see statistics of total number of reviews or stalls created | keep track of my performance and targets for the year | No |
* |
user | include custom rating metrics on my review (star system? Health benefits?) | be more nuanced on my review | No (Current rating system is by star only) |
* * * |
user | include stall opening and closing times | plan my schedule on when to visit the stall accordingly | Yes (by Tag) |
* |
impatient user | manage up to 1000 stalls and reviews in reasonable time | minimize my waiting time | Yes |
* |
impatient user | open the app quickly | not wait so long | Yes |
* |
impatient user | get a visualization for any loading times | know how long I need to wait | No |
* * |
user | search for past reviews by substring | see places I’ve been to before | No (Exact string match as of now by Stall name) |
* * |
user | filter for past reviews by stall | see places I’ve been to before | Yes (find Review by stall name) |
* |
advanced user | add command aliases | speed up my workflow | No |
* |
user | plan my social media posting schedule of my current reviews (i.e. I want to post about this stall at this future date) |
manage my social media presence | No |
* |
advanced user | access data files in csv format | modify the data files used in the app programmatically to do custom things | No (Current user however, can edit the JSON file) |
Use cases
Definition:
- For all use cases below, the System is
FoodWhere
and the Actor is theUser
, unless specified otherwise.- More specifically, the
User
are Food Critics.
Guarantees:
- For any use cases below that changes any data, FoodWhere will guarantee that the data is updated and saved.
Use case 1: Add a food stall
MSS
- User starts FoodWhere.
- User requests to add a stall through an appropriate command.
-
FoodWhere adds the stall and confirms with a success message that the stall is added.
Use case ends.
Extensions
- 2a. User issues the correct command with the wrong syntax.
-
2a1. FoodWhere sends an error message to the User, indicating that the syntax is incorrect, and attaches the correct syntax format in the message.
Use case ends.
-
Use case 2: Add a food review
MSS
- User starts FoodWhere.
- User requests to add a review through an appropriate command.
-
FoodWhere adds the review and confirms with a success message that the review is added.
Use case ends.
Extensions
- 2a. User issues the correct command with the wrong syntax
-
2a1. FoodWhere sends an error message to the User, indicating that the syntax is incorrect, and attaches the correct syntax format in the message.
Use case ends.
-
Use case 3: Delete a food stall
MSS
- User starts FoodWhere.
- User requests to delete a stall through an appropriate command.
-
FoodWhere deletes the stall and confirms with a success message that the stall is deleted.
Use case ends.
Extensions
- 2a. FoodWhere detects an error in the entered data. (Invalid index)
-
2a1. FoodWhere sends an error message to the User, indicating the index used for the delete command is incorrect, and attaches the correct syntax format in the message.
Use case ends.
-
Use case 4: Delete a food review
MSS
- User starts FoodWhere.
- User requests to delete a review through an appropriate command.
-
FoodWhere deletes the review and confirms with a success message that the review is deleted.
Use case ends.
Extensions
- 2a. FoodWhere detects an error in the entered data. (Invalid index)
-
2a1. FoodWhere sends an error message to the User, indicating the index used for the delete command is incorrect, and attaches the correct syntax format in the message.
Use case ends.
-
Use case 5: List food stalls
MSS
- User starts FoodWhere.
- User enters the command to list food stalls.
-
FoodWhere displays all food stalls.
Use case ends.
Extensions
- 2a. User uses list food stalls command with the wrong syntax.
-
2a1. FoodWhere sends an error message to the User, indicating that the syntax is incorrect, and attaches the correct syntax format in the message.
Use case ends.
-
Use case 6: List food reviews
MSS
- User starts FoodWhere.
- User enters the command to list food reviews.
-
FoodWhere displays all food reviews.
Use case ends.
Extensions
- 2a. User uses list food reviews command with the wrong syntax.
-
2a1. FoodWhere sends an error message to the User, indicating that the syntax is incorrect, and attaches the correct syntax format in the message.
Use case ends.
-
Use case 7: Edit a food stall
MSS
- User starts FoodWhere.
- User enters the command to list food stalls.
- FoodWhere displays all food stalls.
- User enters the command to edit a particular food stall.
-
FoodWhere edits the food stall and confirms with a success message that the food stall has been edited.
Use case ends.
Extensions
- 4a. FoodWhere detects an error in the entered data. (Invalid syntax or data)
-
4a1. FoodWhere sends an error message to the User, indicating the syntax or data used for the edit food stall command is incorrect, and attaches the correct format in the message.
Use case ends.
-
Use case 8: Edit a food review
MSS
- User starts FoodWhere.
- User enters the command to list food reviews.
- FoodWhere displays all food reviews.
- User enters the command to edit a particular food review.
-
FoodWhere edits the food review and confirms with a success message that the food review has been edited.
Use case ends.
Extensions
- 4a. FoodWhere detects an error in the entered data. (Invalid syntax or data)
-
4a1. FoodWhere sends an error message to the User, indicating the syntax or data used for the edit food review command is incorrect, and attaches the correct format in the message.
Use case ends.
-
Use case 9: Sort food stalls
MSS
- User starts FoodWhere.
- User enters the command to sort food stalls by specified criterion.
-
FoodWhere displays all food stalls, sorted by the specified criterion.
Use case ends.
Extensions
- 2a. FoodWhere detects an error in the entered data. (Invalid criterion)
-
2a1. FoodWhere sends an error message to the User, indicating that the syntax is incorrect, and attaches the correct syntax format in the message.
Use case ends.
-
Use case 10: Sort food reviews
MSS
- User starts FoodWhere.
- User enters the command to sort food reviews by specified criterion.
-
FoodWhere displays all food reviews, sorted by the specified criterion.
Use case ends.
Extensions
- 2a. FoodWhere detects an error in the entered data. (Invalid criterion)
-
2a1. FoodWhere sends an error message to the User, indicating that the syntax is incorrect, and attaches the correct syntax format in the message.
Use case ends.
-
Use case 11: Find food stalls
MSS
- User starts FoodWhere.
- User enters the command to find food stalls.
-
FoodWhere displays the food stalls that matches the search keywords.
Use case ends.
Extensions
- 2a. User chooses to search from at least one of the following fields:
- Name
-
Tag
Use case resumes from step 3.
- 2b. User uses the find food stalls command with the wrong syntax.
-
2b1. FoodWhere sends an error message to the User, indicating that the syntax is incorrect, and attaches the correct syntax format in the message.
Use case ends.
-
Use case 12: Find food reviews
MSS
- User starts FoodWhere.
- User enters the command to find food reviews.
-
FoodWhere displays the food reviews that matches the search keywords.
Use case ends.
Extensions
- 2a. User chooses to search from at least one of the following fields:
- Name
-
Tag
Use case resumes from step 3.
- 2b. User uses find food reviews command with the wrong syntax.
-
2b1. FoodWhere sends an error message to the User, indicating that the syntax is incorrect, and attaches the correct syntax format in the message.
Use case ends.
-
Use case 13: Clearing data
Preconditions
- User is currently using FoodWhere.
MSS
- User enters a command to clear data from FoodWhere.
-
FoodWhere sends a confirmation message that all data is cleared and saves all changes to disk.
Use case ends.
Use case 14: Editing the data file
MSS
- User modifies the data file.
- User starts FoodWhere.
-
FoodWhere displays the data from the data file.
Use case ends.
Extensions
- 1a. User makes an error in the data file.
- 1a1. User starts FoodWhere.
-
1a2. FoodWhere starts without data.
Use case ends.
Use case 15: Exiting the program
Preconditions
- User is currently using FoodWhere.
MSS
- User enters a command to exit FoodWhere.
-
FoodWhere saves all changes to disk and closes.
Use case ends.
Non-functional requirements
Data requirements
- FoodWhere should be released in a single JAR file which can run independently without installation.
- FoodWhere’s JAR file should be at most 100MB.
- FoodWhere’s documentation should be a static PDF.
- FoodWhere’s documentation should be at most 15MB per file.
- FoodWhere’s documentation should include appropriate UML diagrams.
- FoodWhere’s application data should be stored locally in human editable text files.
- FoodWhere’s application data should be stored in the directory containing the FoodWhere JAR file, or a subdirectory of the directory containing the FoodWhere JAR file.
Environment requirements
- FoodWhere should work on any mainstream OS with Java
11
installed. - FoodWhere should work on both 32-bit and 64-bit environments.
- FoodWhere should assume one local user.
Accessibility requirements
- FoodWhere’s UI should not play audio.
- FoodWhere should be usable without initializing a user account.
- FoodWhere should be able to run completely locally on the device.
- FoodWhere should be faster to use by solely typing (compared to a combination of typing and using the mouse) for a user with above average typing speed for regular English text.
- FoodWhere’s GUI should be optimal on standard screen resolutions 1920x1080 and higher, and for screen scales 100% and 125%.
- FoodWhere’s GUI should be usable for resolutions 1280x720 and higher, and for screen scale 150%.
- FoodWhere’s GUI should have enough information for a new user to start using after typing the
help
command. - FoodWhere should be developed in American English.
- All UI components in FoodWhere need to be recognizable by a user who has used GUIs before.
- FoodWhere should not access other devices present on the system, such as a printer or a scanner.
Business/domain rules
- FoodWhere needs to support multiple reviews on the same stall.
- FoodWhere needs to support stalls with generic names.
- FoodWhere needs to support stalls of different names at the same address.
Performance requirements
- FoodWhere should hold up to 1000 stalls and 1000 reviews while handling each command in under 1 second, on reasonable device specifications.
- FoodWhere’s GUI should be functional within 5 seconds of starting, on reasonable device specifications.
- FoodWhere should be closed within 5 seconds of termination, on reasonable device specifications.
Fault tolerance requirements
- No ASCII text input for FoodWhere should terminate FoodWhere unexpectedly. An exception to this would be the
exit
command. - FoodWhere’s application data should be saved after each successfully completed command which modifies the data.
Other requirements
- Images used in FoodWhere’s UI need to adhere to copyright.
- FoodWhere’s codebase should be following the Object-Oriented paradigm primarily.
- FoodWhere’s development must be strictly conducted under a framework where milestones must be met without delays and each milestone (v1.2, v1.3, v1.4) must have a usable product.
- FoodWhere should be developed with respect to its very specific target user profile.
- FoodWhere should be developed in a team.
Glossary
- Mainstream OS: Windows, Linux, Unix, OS-X
- User: Refers to the food critic
- Command: Input by users that is within the Command List
- Review: Refers to an entry for a particular food stall
Appendix: Instructions for manual testing
Given below are instructions to test the app manually.

Launch and shutdown
-
Initial launch
-
Download the JAR file and copy into an empty folder.
-
Double-click the JAR file.
Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
-
-
Saving window preferences
Prerequisites: The JAR file is in a folder that is not write-protected.
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the JAR file.
Expected: The most recent window size and location is retained.
-
-
Exiting the program
- Enter
exit
command in command input box or click on the X button.
Expected: The application closes.
- Enter
Adding a stall
-
Adding a stall
-
Prerequisites: None
-
Test case:
sadd n/Ah Kim Chicken Rice a/311, Clementi Ave 2, #02-25 t/chickenRice t/opensDaily
Expected: A new stall is added to the list with tagschickenrice
andopensdaily
as tags are saved in lowercase. Details of the newly added stall shown in the status message. GUI updates to show the newly added stall and its details. -
Test case:
sadd n/Ah Kim Chicken Rice
Expected: No stall is added. Error details shown in the status message. GUI does not update. -
Other incorrect stall add commands to try:
sadd
,sadd a/311, Clementi Ave 2, #02-25
,sadd n/Kim's Chicken a/311, Clementi Ave 2, #02-25
Expected: Similar to previous.
-
Adding a review
-
Adding a review to a stall
-
Prerequisites: At least one stall entry exists.
-
Test case:
radd s/1 d/20/09/2022 c/The food was good, the chicken rice was fresh. r/4 t/opensDaily t/worthyTrip
Expected: A new review is added to the list as a review of the first stall with tagsopensdaily
andworthytrip
as tags are saved in lowercase. Details of the newly added review shown in the status message. GUI updates to show the newly added review and its details. -
Test case:
radd s/1 d/20/09/2022
Expected: No review is added. Error details shown in the status message. GUI does not update. -
Other incorrect review add commands to try:
radd
,radd s/1
,radd s/1 c/The food was good
,radd d/20/09/2022 c/The food was good, the chicken rice was fresh.
Expected: Similar to previous.
-
Listing all stalls
-
Lists all stalls on GUI
-
Prerequisites: None
-
Test case:
slist
Expected: GUI updates and all stalls are listed.
-
Listing all reviews
-
Lists all reviews on GUI
-
Prerequisites: None
-
Test case:
rlist
Expected: GUI updates and all reviews are listed.
-
Deleting a stall
-
Deleting a stall while all stalls are being shown
-
Prerequisites: List all stalls using the
slist
command. Multiple stalls in the list. -
Test case:
sdel 1
Expected: First stall is deleted from the list. Details of the deleted stall shown in the status message. GUI updates to remove the deleted stall and its associated reviews. -
Test case:
sdel 0
Expected: No stall is deleted. Error details shown in the status message. GUI does not update. -
Other incorrect stall delete commands to try:
sdel
,sdel a
,sdel -1
,sdel x
(where x is larger than the list size)
Expected: Similar to previous.
-
Deleting a review
-
Deleting a review while all reviews are being shown
-
Prerequisites: List all reviews using the
rlist
command. Multiple reviews in the list. -
Test case:
rdel 1
Expected: First review is deleted from the list. Details of the deleted review shown in the status message. GUI updates to remove the deleted review. -
Test case:
rdel 0
Expected: No review is deleted. Error details shown in the status message. GUI does not update. -
Other incorrect review delete commands to try:
rdel
,rdel a
,rdel -1
,rdel x
(where x is larger than the list size)
Expected: Similar to previous.
-
Editing a stall
-
Editing a stall while all stalls are being shown
-
Prerequisites: List all stalls using the
slist
command. Multiple stalls in the list. -
Test case:
sedit 1 n/Alice Chicken Rice
Condition: There must not exist a stall with the same name and address as the edited stall’s name and address
Expected: The name of the first stall and its associated reviews edited. Details of the edited stall shown in the status message. GUI updates to show the edited details. -
Test case:
sedit 1 a/Chicken Street 123
Condition: Another stall must not have the same name and address as the edited stall’s name and address
Expected: The address of the first stall and its associated reviews edited. Details of the edited stall shown in the status message. GUI updates to show the edited details. -
Test case:
sedit 1 n/Alice's Chicken Rice
Expected: No stall is edited. Error details shown in the status message. GUI does not update. -
Other incorrect stall edit commands to try:
sedit
,sedit 1
,sedit x n/Alice Chicken Rice
,sedit x a/Chicken Street 123
(where x is larger than the list size)
Expected: Similar to previous.
-
Editing a review
-
Editing a review while all reviews are being shown
-
Prerequisites: List all reviews using the
rlist
command. Multiple reviews in the list. -
Test case:
redit 1 d/20/09/2022
Condition: There must not exist a review on the same stall with the same details (date, content and tags) as the edited review’s details
Expected: The date of the first review edited. Details of the edited review shown in the status message. GUI updates, the edited review is moved to the back of the list with the edited details shown. -
Test case:
redit 1 c/The chicken was dry
Condition: There must not exist a review on the same stall with the same details (date, content and tags) as the edited review’s details
Expected: The content of the first review edited. Details of the edited review shown in the status message. GUI updates, the edited review is moved to the back of the list with the edited details shown. -
Test case:
redit 1 d/2022/09/20
Expected: No review is edited. Error details shown in the status message. GUI does not update. -
Other incorrect review edit commands to try:
redit
,redit 1
,redit x d/20/09/2022
,redit x c/The chicken was dry
(where x is larger than the list size)
Expected: Similar to previous.
-
Find stalls
-
Finding stalls by name and/or tags
-
Prerequisites: At least one stall entry exists that we are finding with name and/or tag exists.
-
Test case:
sfind n/chicken
Condition: There is a stall with the wordchicken
in its name
Expected: Status message responds with the number of stalls withchicken
as a word in the name. GUI updates to show these stalls. -
Test case:
sfind t/opendaily
Condition: There is a stall with the tagopendaily
Expected: Status message responds with the number of stalls that haveopendaily
as a tag. GUI updates to show these stalls. -
Test case:
sfind n/chicken t/opendaily
Condition: There is a stall with the wordchicken
in its name and/or with the tagopendaily
Expected: Status message responds with the number of stalls withchicken
as a word in the name and/or haveopendaily
as a tag. GUI updates to show these stalls. -
Test case:
sfind n/
Expected: No stalls are found. GUI updates to show no stalls. -
Test case:
sfind n/Alice's
Expected: No stalls are found. Error details shown in the status message. GUI does not update. -
Other incorrect stall find commands to try:
sfind
,sfind a/Ang Mo Kio
Expected: Similar to previous.
-
Find reviews
-
Finding reviews by stall name and/or tags
-
Prerequisites: At least one review entry exists that we are finding with stall name and/or tag exists.
-
Test case:
rfind n/chicken
Condition: There is a review with the wordchicken
in its stall name
Expected: Status message responds with the number of reviews withchicken
as a word in the stall name. GUI updates to show these reviews. -
Test case:
rfind t/delicious
Condition: There is a review with the tagdelicious
Expected: Status message responds with the number of reviews that havedelicious
as a tag. GUI updates to show these reviews. -
Test case:
rfind n/chicken t/delicious
Condition: There is a review with the wordchicken
in its stall name or with the tagdelicious
Expected: Status message responds with the number of reviews withchicken
as a word in the stall name and/or havedelicious
as a tag. GUI updates to show these reviews. -
Test case:
rfind n/
Expected: No reviews are found. GUI updates to show no reviews. -
Test case:
rfind n/Alice's
Expected: No reviews are found. Error details shown in the status message. GUI does not update. -
Other incorrect review find commands to try:
rfind
,rfind a/Ang Mo Kio
Expected: Similar to previous.
-
Sort stalls
-
Sorting stalls by name
-
Prerequisites: Multiple stalls in the list.
-
Test case:
ssort name
Expected: Status message responds that stalls have been sorted by name (A-Z). GUI updates to show sorted stalls. -
Test case:
ssort reversedname
Expected: Status message responds that stalls have been sorted by name (Z-A). GUI updates to show sorted stalls. -
Test case:
ssort address
Expected: Stalls are not sorted. Error details are shown in the status message. GUI does not update. -
Other incorrect stall sort commands to try:
ssort
,ssort abc
Expected: Similar to previous.
-
Sort reviews
-
Sorting reviews by stall name, date or rating
-
Prerequisites: Multiple reviews in the list.
-
Test case:
rsort name
Expected: Status message responds that reviews have been sorted by name (A-Z). GUI updates to show sorted reviews. -
Test case:
rsort reversedname
Expected: Status message responds that reviews have been sorted by name (Z-A). GUI updates to show sorted reviews. -
Test case:
rsort date
Expected: Status message responds that reviews have been sorted by date (oldest to newest). GUI updates to show sorted reviews. -
Test case:
rsort reverseddate
Expected: Status message responds that reviews have been sorted by date (newest to oldest). GUI updates to show sorted reviews. -
Test case:
rsort rating
Expected: Status message responds that reviews have been sorted by rating (lowest to highest). GUI updates to show sorted reviews. -
Test case:
rsort reversedrating
Expected: Status message responds that reviews have been sorted by rating (highest to lowest). GUI updates to show sorted reviews. -
Test case:
rsort content
Expected: Reviews are not sorted. Error details are shown in the status message. GUI does not update. -
Other incorrect review sort commands to try:
rsort
,rsort abc
Expected: Similar to previous.
-
Saving data
-
Dealing with missing/corrupted data files
-
Test case: Simulate data file is missing by deleting
foodwhere.json
in data folder.
Expected: Upon launching FoodWhere, the GUI displays the default stalls and reviews list. -
Test case: Simulate data file goes corrupt by editing a data field to an invalid value while FoodWhere is running.
Condition: No command that edits data is executed after editing the data file with invalid value
Expected: FoodWhere runs normally and does not crash. However, upon closing and re-launching the program, the GUI displays an empty stalls and reviews list.
-
-
Commands that change data will modify data file
- Test case: Run any command that affects the data. E.g.
sadd
,radd
,sdel
,rdel
and exit the program with theexit
command.
Expected: Upon re-launching FoodWhere, the edits made to the data are preserved.
- Test case: Run any command that affects the data. E.g.