{ list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well }
Refer to the guide Setting up and getting started.
The Architecture Diagram given above explains the high-level design of the application.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
Main
(consisting of classes Main
and MainApp
) is in charge of the app launch and shut down.
The bulk of the app's work is done by the following four components:
UI
: The UI of the application.Logic
: The command executor.Model
: Holds the data of the application in memory.Storage
: Reads data from, and writes data to, the hard disk.Commons
represents a collection of classes used by multiple other 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 library manager issues the command delete 1
.
Each of the four main components (also shown in the diagram above),
interface
with the same name as the Component.{Component Name}Manager
class (which follows the corresponding API interface
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.
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
, PersonListPanel
, 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,
Logic
component.Model
data so that the UI can be updated with the modified data.Logic
component, because the UI
relies on the Logic
to execute commands.Model
component, as it displays Person
object residing in the Model
.API : Logic.java
Here's a (partial) class diagram of the Logic
component:
The following is a partial class diagram showing the interaction between the abstract Command class and the various command classes.
The sequence diagram below illustrates the interactions within the Logic
component, taking execute("delete 1")
API call as an example.
Note: The lifeline for DeleteCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
How the Logic
component works:
Logic
is called upon to execute a command, it is passed to an AddressBookParser
object which in turn creates a parser that matches the command (e.g., DeleteCommandParser
) and uses it to parse the command.Command
object (more precisely, an object of one of its subclasses e.g., DeleteCommand
) which is executed by the LogicManager
.Model
when it is executed (e.g. to delete a person).Model
) to achieve.CommandResult
object which is returned back from Logic
.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:
AddressBookParser
class creates an XYZCommandParser
(XYZ
is a placeholder for the specific command name e.g., AddCommandParser
) which uses the other classes shown above to parse the user command and create a XYZCommand
object (e.g., AddCommand
) which the AddressBookParser
returns back as a Command
object.XYZCommandParser
classes (e.g., AddCommandParser
, DeleteCommandParser
, ...) inherit from the Parser
interface so that they can be treated similarly where possible e.g, during testing.API : Model.java
The Model
component,
Person
objects (which are contained in a UniquePersonList
object).Person
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiable ObservableList<Person>
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.UserPref
object that represents the user’s preferences. This is exposed to the outside as a ReadOnlyUserPref
object.Library
object that represents the library data. This is exposed to the outside as a ReadOnlyLibrary
object.Model
represents data entities of the domain, they should make sense on their own without depending on other components).The Person
data,
Name
, Phone
, Email
, Address
, list of Tag
objects, MeritScore
, list of Book
objects.The Library
data
Book
objects and Threshold
Book
objects are exposed to outsiders as an unmodifiable ObservableList<Book>
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.`Note: An alternative (arguably, a more OOP) model is given below. It has a Tag
list in the AddressBook
, which Person
references. This allows AddressBook
to only require one Tag
object per unique tag, instead of each Person
needing their own Tag
objects.
Similarly, Library
can also have a list of known Book
objects separate from the list of available Book
objects that can help keep track of the count of a Book
. This way, new Book
objects will not have to be created for duplicate books.
How the library updates in Model:
Person
object corresponding to the index.Merit Score
is retrieved from the Person
object and compared to Threshold
from the Library
to check if Person
can borrow a book.Person
can borrow a book, the Book
object is removed from the list of available books in Library
object and added to the book list in Person
object.API : Storage.java
The Storage
component,
AddressBookStorage
and UserPrefStorage
, which means it can be treated as either one (if only the functionality of only one is needed).Model
component (because the Storage
component's job is to save/retrieve objects that belong to the Model
).LibraryStorage
currently works separately from Storage
but there are plans for Storage
to inherit from LibraryStorage
in the Future Enhancements).Classes used by multiple components are in the seedu.addressbook.commons
package.
This section describes some noteworthy details on how certain features are implemented.
Merit Score Attribute:
Person
class.Operations Affecting Merit Score:
Threshold Check Before Borrowing:
Threshold Setting:
limit
command.These changes aim to regulate borrowing behavior, preventing excessive borrowing and ensuring fair access to library resources based on library user's credibility as measured by their merit score.
Library Class:
LibraryStorage Class:
These two classes work together to provide functionality for managing a library's collection of books, with Library
handling operations directly related to book management and LibraryStorage
handling file I/O operations.
This separation of concerns helps in keeping the code modular and maintainable.
Library
now acts as a similar entity to the AddressBook
and UserPrefs
and is now composited into Model
, and implements the ReadOnlyLibrary Interface.
Model
now contains useful Library
operations such as:
Threshold
operations.Book
operations on the book list in a library.This command is facilitated through the use of Threshold
as an attribute in the Library
class.
Any library user has to have a Merit Score
greater or equal to the set Threshold
in order to borrow from the Library
.
As Threshold
is now an attribute of Library
, the library user's ability to borrow now depends on the Library instance and not within the Borrow Command.
Limit Command sets the Threshold
of the Library
, resulting in all library users being affected by the change at the same time when the Limit Command is called.
The default value of a Threshold
is set as -3
. Any calls to the Limit Command with the same value of the current Threshold
will result in a duplicate threshold detected message.
Library
now has a method to check if a library user can borrow a book from the library by internally comparing the user's Merit Score
and the library's Threshold
. Borrow Command now utilises this function to check if a user is able to Borrow a book from the Library instead of handling the check within the Borrow Command itself.
Library managers can retroactively disallow library users from borrowing books from the Library, with library users having to meet the limit set before being able to borrow again.
Merit Score
:0.Merit Score
: -2.limit 0
.
Threshold
: 0.Merit Score
: 0 (greater than or equal to Threshold
).Merit Score
: -2 (less than Threshold
).limit -2
.
Threshold
: -2.Merit Score
: 0 (greater than or equal to Threshold
).Merit Score
: -2 (greater than or equal to Threshold
).Threshold
: -2.Merit Score
: 0 (greater than or equal to Threshold
).Merit Score
: -3 (greater than or equal to Threshold
).limit 1
.
Threshold
: 1.Merit Score
: 0 (less than Threshold
).Merit Score
: -3 (less than Threshold
).Threshold
: 1Merit Score
: 1 (greater than or equal to Threshold
).Merit Score
: -2 (less than Threshold
).It is also plausible for Threshold
to be implemented as an attribute within each library user.
This would also change the implementation for the limit
command to now individually set limits to each specified library user.
This would give the library manager greater flexibility to vary each of the library user's individual ability to borrow books.
This implementation was decided against as setting a standardised limit would give an easier time for library managers to manage all library users at the same time, and not having to individually manage each user's Threshold
.
Individual library user's ability to borrow can also be increased and decreased indirectly by changing the library user's merit score.
Note: the library user's Merit Score cannot be decreased without altering the library user's borrowing book list.
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 library manager launches the application for the first time. The VersionedAddressBook
will be initialised with the initial address book state, and the currentStatePointer
pointing to that single address book state.
Step 2. The library manager 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.
Step 3. The library manager 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
.
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 4. The library manager 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.
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 library manager rather
than attempting to perform the undo.
The following sequence diagram shows how an undo operation goes through the Logic
component:
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.
Similarly, how an undo operation goes through the Model
component is shown below:
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.
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 library manager rather than attempting to perform the redo.
Step 5. The library manager 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.
Step 6. The library manager 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.
The following activity diagram summarises what happens when a library manager executes a new command:
Aspect: How undo & redo executes:
Alternative 1 (current choice): Saves the entire address book.
Alternative 2: Individual command knows how to undo/redo by itself.
delete
, just save the person being deleted).EMAIL
parameter.
INDEX
and THRESHOLD
to be parsed within the boundaries of Java's Integer.MIN_VALUE
and Integer.MAX_VALUE
.NAME
, PHONE-NUMBER
, EMAIL
, ADDRESS
, TAG
, KEYWORD
, BOOKTITLE
to a reasonable length.Book
class.
Book
, the library manager can specifically choose which book to use in the commands.NAME
field are exactly the same.PHONE
or EMAIL
are exactly the same as they are unique identifiers and not NAME
.NAME
to be case-insensitive (John Doe and john doe are the same person).NAME
differs by only by white spaces (John Doe and John Doe are similar and could be duplicates).clear
command is executed to use the words "Contact List" instead of "Address book" for clarity.INDEX
entered by library manager is greater than the length of the Contact List to be clearer (e.g. Index is larger than the number of people in the list).borrow
, return
, edit
.
return
command).LibraryStorage
class outside of StorageManager
class that handles the data for the Library
.LibraryStorage
to inside StorageManager
to be more consistent with the code architecture.Merit Score
has an extra 's
in the word User's
.Target user profile:
Value proposition: manage library users and keeps track of borrowing and returning of books faster than a typical mouse/GUI driven app.
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
.
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
* * * | new library manager | see usage instructions | refer to instructions when I forget how to use the App. |
* * * | library manager | add a new library user | record a new user's information. |
* * * | library manager | delete a library user | remove entries that I no longer need. |
* * * | library manager | find a library user by name | locate details of persons without having to go through the entire list. |
* | library manager with many users in the Contact List | sort library user by name | locate a person easily. |
* * * | library manager | record the phone number of the library user | send SMS reminders to notify them that someone else is looking for the book. |
* * * | library manager | record the email address of the library user | send an email reminders to notify them that someone else is looking for the book. |
* * * | library manager | record the postal address of the library user | send a warning letter when breaching community guidelines. |
* * * | library manager | record the book title of all books in the library | keep track of the books available in the library at the moment. |
* * * | library manager | record the book title the library user has borrowed | keep track of the books the borrower has borrowed. |
* * | library manager | be able to decide the threshold merit score for the library | decide the limit of books to borrow to the users. |
(For all use cases below, the System is the MyBookshelf
and the Actor is the Community Library Manager (CLM)
, unless specified otherwise)
Use case ends
2a. MyBookshelf detects an error in the information entered.
Use case resumes from step 3
*a. At any time, library manager chooses to cancel the addition of user.
Use case ends
Use case ends
*a. At any time, library manager chooses to cancel the addition of library user.
Use case ends.
Use case ends
2a. MyBookshelf detects invalid index.
Use case resumes from step 2.
2b. MyBookshelf detects an error in the entered information.
Use case resumes from step 3.
*a. At any time, library manager chooses to cancel the addition of the library user.
Use case ends.
Use case ends
*a. At any time, Library manager chooses to cancel the finding process of library users.
Use case ends.
Use case ends
2a. MyBookshelf detects an invalid index.
Use case resumes from step 2.
*a. At any time, library manager chooses to cancel the process of deleting the library user.
Use case ends.
Use case ends
3a. MyBookshelf detects an invalid index for library user.
Use case resumes from step 3.
3b. MyBookshelf detects an error in the book title.
Use case resumes from step 3.
*a. At any time, library manager chooses to cancel the borrowing process of the library user.
Use case ends.
Use case ends
3a. MyBookshelf detects invalid index for library user.
Use case resumes from step 3.
3b. MyBookshelf detects an error in the book title.
Use case resumes from step 3.
*a. At any time, Library manager chooses to cancel the returning process of the library user.
Use case ends.
Use case ends
3a. MyBookshelf detects invalid index for the library user.
Use case resumes from step 3.
3b. MyBookshelf detects an error in the book title.
Use case resumes from step 3.
*a. At any time, library manager chooses to cancel the donation process.
Use case ends.
Use case ends
2b. MyBookshelf detects an error in the book title.
Use case resumes from step 2.
*a. At any time, library manager chooses to cancel the addition process.
Use case ends.
Use case ends
2b. MyBookshelf detects an error in the book title.
Use case resumes from step 2.
*a. At any time, Library manager chooses to cancel the deletion process.
Use case ends.
Use case ends
*a. At any time, library manager chooses to cancel the lookup process.
Use case ends.
Use case ends
2a. MyBookshelf detects invalid limit.
Use case resumes from step 3.
*a. At any time, library manager chooses to cancel the setting of limit.
Use case ends.
Use case ends
*a. At any time, library manager chooses to cancel the process of clearing the contact list.
Use case ends.
Use case ends
*a. At any time, library manager chooses to cancel the process of accessing the help window.
Use case ends.
Use case ends
*a. At any time, library manager chooses to cancel the exit process.
Use case ends.
11
or above installed.BOOKTITLE
. Appears in both the Library User
's book list and the Library Book List
.Library User
. This score provides an estimate of the number of books a library user can borrow.Book
(s) currently stored in the MyBookshelf application. It appears in the right column of the User Interface. Sometimes referred as "library" or "available books".Given below are instructions to test the app manually.
Note: These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.
Initial launch.
mybookshelf.jar
and save it into an empty folder.cd
) where mybookshelf.jar
is saved.java -jar mybookshelf.jar
.Saving window preferences.
Exiting the app using exit
command.
exit
to the command box.Exiting the app by clicking the close button.
Exiting the app by clicking the Exit
button in File
tab
File
tab.Exit
button.Warning: The following sections assume that you are using the sample data populated with the initial start-up. Follow the steps in order for the desired results.
Deleting a library user with all library users in the contact list shown.
list
command.delete 6
.Attempting to delete a library user of an invalid index.
delete 6
.Attempting to use the delete
command inappropriately.
delete
commands to try: delete
, delete x
, ...
(where x is any invalid parameter).Add a book into the library book list.
BOOKTITLE
must not contain the character sequence b/
within it. (Note the whitespace before 'b').addbook b/Percy Jackson
.Attempting to add a book without a title.
addbook b/
.BOOKTITLE
cannot be empty. Error details shown in the status message. Status bar remains the same.Attempting to add a book with b/
within its title.
addbook b/Hello b/World
(BOOKTITLE
is interpreted to be "Hello b/World").b/
is used as the parser for our application, the use of multiple b/
will result in an error message due to the use of multiple b/
.Attempting to use the addbook
command inappropriately.
addbook
commands to try: addbook
, addbook x
, ...
(where x is any invalid parameter).Delete a book from the library book list.
BOOKTITLE
must not contain the character sequence b/
within it. (Note the whitespace before 'b').delbook b/Percy Jackson
.Attempting to delete a book without a title.
delbook b/
.BOOKTITLE
cannot be empty. Error details shown in the status message. Status bar remains the same.Attempting to delete a book that is not in the library.
delbook b/Not In Library
.Not In Library
is not in the library book list.Attempting to use the delbook
command inappropriately.
delbook
commands to try: delbook
, delbook x
, ...
(where x is any invalid parameter).Library user donates a book to the library.
BOOKTITLE
must not contain the character sequence b/
within it. (Note the whitespace before 'b').donate 1 b/Percy Jackson
.Attempting to donate a book without a title.
donate 1 b/
.BOOKTITLE
cannot be empty. Error details shown in the status message. Status bar remains the same.Attempting to donate a book with b/
within its title.
donate 1 b/Hello b/World
(BOOKTITLE
is interpreted to be "Hello b/World").b/
is used as the parser for our application, the use of multiple b/
will result in an error message due to the use of multiple b/
.Attempting to use the donate
command inappropriately.
donate
commands to try: donate
, donate x
, ...
(where x is any invalid parameter).Library user borrows a book from the library.
BOOKTITLE
must not contain the character sequence b/
within it. (Note the whitespace before 'b').borrow 1 b/Percy Jackson
.Attempting to borrow a book without a title.
borrow 1 b/
.BOOKTITLE
cannot be empty. Error details shown in the status message. Status bar remains the same.Attempting to borrow a book with b/
within its title.
borrow 1 b/Hello b/World
(BOOKTITLE
is interpreted to be "Hello b/World").b/
is used as the parser for our application, the use of multiple b/
will result in an error message due to the use of multiple b/
.Attempting to use the borrow
command inappropriately.
borrow
commands to try: borrow
, borrow x
, ...
(where x is any invalid parameter).Library user returns a book to the library.
BOOKTITLE
must not contain the character sequence b/
within it. (Note the whitespace before 'b').return 1 b/Percy Jackson
.Attempting to return a book without a title.
return 1 b/
.BOOKTITLE
cannot be empty. Error details shown in the status message. Status bar remains the same.Attempting to return a book that is not in the library user's book list.
return 1 b/Not In Book List
.Not In Book List
is not in the library user's book list.Attempting to use the return
command inappropriately.
return
commands to try: return
, return x
, ...
(where x is any invalid parameter).THRESHOLD
. Default THRESHOLD
is -3
.limit
.THRESHOLD
of the library.Setting a new threshold to the library.
THRESHOLD
. The new THRESHOLD
is an integer between -2147483648
and 2147483647
. Provided the new THRESHOLD
is different from the old THRESHOLD
.limit -3
(Which is the same as the current default THRESHOLD
).THRESHOLD
remains the same.Setting a new threshold to the library.
limit 0
.THRESHOLD
of the library is set to 0
.Dealing with missing library user's data file.
data/addressbook.json
.data/addressbook.json
.data/addressbook.json
file.Dealing with corrupted library user's data file.
data/addressbook.json
.data/addressbook.json
.data/addressbook.json
.data/addressbook.json
file.Dealing with missing library book list's data file.
data/library.txt
.data/library.txt
.data/library.txt
file.Dealing with corrupted library book list's data file.
data/library.txt
.data/library.txt
.data/library.txt
.data/library.txt
.Saving library user's data.
data/addressbook.json
. Data in data file is valid.Saving library book list's data.
data/library.txt
. Data in data file is valid.