PropertyRentalManager.jar
from here.cd [PATH_TO_JAR_DIRECTORY]
java -jar PropertyRentalManager.jar
The list feature has the following commands in it -
list -client
This lists every client, along with all their informationlist -property
This lists every property, along with all its informationlist -everything
This lists everything about both clients and propertieslist -client TAG
This lists only the information present in the TAG for every client. The types of
TAGs are -
c/
This is for contact numberb/
This is for budgetn/
This is for namee/
This is for e-mail-short
This is for the shorthand version(displays just name and budget)list -property TAG
This lists only the information present in the TAG for every property. The types
of TAG are -
a/
This is for addressn/
This is for namep/
This is for pricet/
This is for unit type-short
This is for the shorthand version(displays address, price and unit type)list -pair
This lists all clients and properties that have been paired, in no particular order.list -pair -short
This lists the -short version of all clients and properties that have been paired, in
no particular orderThere are 7 different classes, which each inherit from the Command class, and work in similar ways -
API: pairingList.java
PairingList
is responsible for recording which clients are renting which property.PairingList
does not inherit from other classes. It stores references to Client
and Property
objects.Here is how classes involved in the pairing/unpairing actions interact with each other:
CommandPairParser
and CommandUnpairParser
inherit from a general CommandPairUnpairParser
, which contains
parsing methods that are common to its subclasses.CommandPairParser
and CommandUnpairParser
are responsible for checking input format. After (successful) checking,
they create CommandPair
and CommandUnpair
objects respectively.CommandPair
and CommandUnpair
contain references to ClientList
and PropertyList
because the command classes
need to validate user input against the data in ClientList
and PropertyList
.PairingList
is updated with the new pairings. Storage
records these changes and Ui
prints the confirmation message for the user action.Here is the underlying data structure of PairingList
:
PairingList
is essentially a “wrapper” to the underlying java.util.HashMap
. The key-value pairs in the hashmap have
Client
as the key and Property
as the value.PairingList
provides methods to add or delete these key-value pairs to represent the pairing and unpairing of real-life
clients and properties.Client
and Property
references contained in PairingList
must be references to valid Client
and Property
objects in ClientList
and PropertyList
as well, since PairingList
is an implementation of an adjacency list.For Storage
feature:
The Storage class is a superclass itself that is not inherit from other class. This class is responsible for managing three different text file:
client.txt
- Stores the client that is in the Client ArrayList.property.txt
- Stores the property that is in the Property ArrayList.pairing.txt
- Stores the relationship between a client and property which is stored in the Pairing hashmap.It has an association with other class which includes:
Since the arraylist changes by adding and deleting operations while hashmap changes by pair and unpair
operations, the text files will be updated when add
, delete
, pair
or unpair
is invoked.
This section describes the implementation details of the features within Property Rental Manager.
The add feature adds an entity to its corresponding list. For Property Rental Manager, there are two variations to the add feature, namely add -client
and add -property
.
add -client
: Add a new client to the client list.add -property
: Add a new property to the property list.The implementation of add feature can be simplified into two major sections. The first section involves the parsing and validation of relevant information provided by the user while the second section comprises the actual addition of client/property to the client list/property list.
Section 1: Parse and Validation of Information
The first section is facilitated by the following classes:
CommandAddParser
: Contains common methods used by CommandAddClientParser
and CommandAddPropertyParser
.CommandAddClientParser
: Extracts and validates client information from commandDescription
(User Input).CommandAddPropertyParser
: Extracts and validates property information from commandDescription
(User Input).The following is a simple class diagram of the three classes:
Command Add Parser Related Classes Diagram
As shown above, both CommandAddClientParser
and CommandAddPropertyParser
classes have a similar core method called parseCommand()
which is responsible for client or property detail extraction and validation. The rest of the methods in both classes are sub-methods of the parseCommand()
method.
Also, most of the sub-methods are used to perform validations on the extracted details. Many of them are implemented via regex pattern checker.
checkForValidSingaporeContactNumber(String)
checkForValidEmail(String)
checkForBudgetNumberFormat(String)
checkForDuplicateClient(ClientList, ArrayList<String>)
checkForValidSingaporeAddress(String)
checkForValidSingaporeLandedPropertyAddress(String)
checkForValidSingaporeBuildingAddress(String)
checkForPriceNumberFormat(String)
checkForValidUnitType(String)
checkForValidAddressFormatUnitTypeMatching(String, String)
checkForDuplicateProperty(PropertyList, String)
checkForEmptyDetails(String)
: Checks for any missing essential details, non-essential detail such as optional email can be empty.Note: Since the target user is a property manager working in Singapore, some validations are tailored to Singapore context.
Section 2: Addition of client or property to client list or property list
The second section is facilitated by the following classes:
CommandAdd
: Abstract superclass of CommandAddClient
and CommandAddProperty
classes.CommandAddClient
: Creates a Client
object and add it to the clientList
.CommandAddProerty
: Creates a Property
object and add it to the propertyList
.The following is a simple class diagram of the three classes:
Command Add Related Classes Diagram
As shown above, both CommandAddClient
and CommandAddProperty
classes have a similar core method called execute(...)
which is responsible for the new client or property addition into their respective lists.
Example Scenario
Given below is an example scenario on how add client/property behaves at each step.
Step 1: The user executes add -client n/NAME c/CONTACT_NUMBER e/EMAIL b/BUDGET_MONTH
or add -property n/NAME a/ADDRESS p/PRICE t/TYPE
. Depending on add -client
or add -property
specified, a Parser
object of type CommandAddClientParser
or CommandAddPropertyParser
is created.
Step 2: The Parser
object will then call method CommandAddClientParser#parseCommand()
or CommandAddPropertyParser#parseCommand()
which will check for any incorrect formatting before the extraction and validation of client/property details.
Step 3: If there is no error, a Command
object of type CommandAddClient
or CommandAddProperty
is created.
Step 4: Next, the Command
object will then call method CommandAddClient#execute(...)
or CommandAddProperty#execute(...)
which will add a new Client
or Property
type object created into their respective clientList
/propertyList
.
Step 5: Lastly, method Ui#showClientAddedConfirmationMessage()
or Ui#showPropertyAddedConfirmationMessage()
is called to notify user about the successful addition of new client or property. Also, method Storage#addToClientFile
or Storage#addToPropertyFile
is called to update their respective storage files.
The following are simplified sequence diagrams of add feature for client and property:
Add Client Sequence Diagram
Add Property Sequence Diagram
The delete client/property mechanism involves the following classes: CommandDeleteClientParser
,
CommandDeletePropertyParser
, CommandDeleteClient
, CommandDeleteProperty
,
ClientList
, PropertyList
and PairingList
.
Given below is an example usage scenario and how the delete client/property behaves at each step.
Step 1: The user executes delete -client i/INDEX
or delete -property i/INDEX
.
The CommandDeleteClientParser
or CommandDeletePropertyParser
class is called respectively and the format of the user
input is checked for any incorrect formatting.
Step 2: If there are no errors, CommandDeleteClient
or CommandDeleteProperty
is called respectively.
The CommandDeleteClient#execute()
or CommandDeleteProperty#execute()
method is then called.
Step 3: The ClientList#deleteClient()
or PropertyList#deleteProperty()
method is called which
removes the Client or Property with that specific index from their respective ArrayList.
Step 4: Any pairings involving that specific Client or Property is also deleted using the
pairingList#deletePairing()
method. A message showing all the deleted pairs is shown to the user.
Step 5: The corresponding line(s) in the respective files are deleted. The method is shown in the Storage Implementation section.
The following class diagram shows all the classes involved in the delete client/property operation and their relationships.
The following sequence diagram shows how the delete client operation works, showcasing the
CommandDeleteClient#execute()
method.
The following sequence diagram shows how the delete property operation works, showcasing the
CommandDeleteProperty#execute()
method.
PairingList
facilitates pair and unpair commands by storing client-property pairs.
When a client rents a property, the client and property form a pair.
PairingList
uses a hash map to represent these client-property pairs, where the key is a Client
and the value is Property
.java.util.HashMap
prevents duplicate keys, which dovetails nicely with the fact that real-life tenants (clients) only have
one place of residence at any time.The partial sequence diagram for the pair command, when called from Duke.java
, is shown below:
NOTE: Self-invocations have been omitted to emphasise inter-object method calls.
The pair command takes in user input of the format:
pair ip/PROPERTY_INDEX ic/CLIENT_INDEX
where PROPERTY_INDEX
and CLIENT_INDEX
must be positive integers which are indexes present in ClientList
and PropertyList
, if their arrays were 1-indexed.
How the pair command works:
Parser
(specifically, CommandPairParser
).CommandPairParser
checks the user input for formatting mistakes such as missing flags, wrong flag order and non-integer inputs.CommandPair
object is created.CommandPair
is executed, there are more checks to validate the parsed input against data from PropertyList
and
ClientList
. These checks throw exceptions when the user inputs contains indexes which are not within the internal arrays of
PropertyList
or ClientList
.Property
and Client
objects from
PropertyList
and ClientList
.Client
and Property
objects already match an existing pair, the
Client
is already paired with some other Property
, or when the user pairs a client whose budget is lower than the property’s rental price.Client
and Property
objects are inserted as a pair into the hashmap of PairingList
.Storage
and a confirmation message is shown to the user.The unpair command takes in user input of the format:
unpair ip/PROPERTY_INDEX ic/CLIENT_INDEX
where PROPERTY_INDEX
and CLIENT_INDEX
must be positive integers which are indexes present in ClientList
and PropertyList
, if their private arrays were 1-indexed.
(The sequence diagram for unpair is not provided as the mechanism is similar to that of Pair)
How the unpair command works :
Parser
(specifically, CommandUnpairParser
).CommandUnpairParser
checks the user input for formatting mistakes such as missing flags, wrong flag order and non-integer inputs.CommandUnpair
object is created.CommandUnpair
is executed, there are more checks to validate the parsed input against data from PropertyList
and
ClientList
. These checks throw exceptions when the user inputs list indexes which are not within the internal arrays of
PropertyList
or ClientList
.Property
and Client
objects from
PropertyList
and ClientList
.Client
and Property
objects are not in an existing pair.Client
-Property
pair is deleted from the hashmap of PairingList
.Storage
and a confirmation message is shown to the user.The find command takes in an input from the user in the following format:
find -client f/<QUERY_TEXT>
find -property f/<QUERY_TEXT>
where QUERY_TEXT
refers to the text that will be searched through the Client List when the user indicates -client
and Property List when the user indicates -property
.
The sequence diagram for both find client and find property is as shown below:
The program will iterate through all the entities to search for any matches with the query text.
For clients, it will identify if the query text matches:
For properties, it will identify if the query text matches:
For example, if a query text is “Ken” and the address is “Kent Ridge”, it will be identified as a match since the word is contained within part of the address.
Upon identifying a match, the program will print out the message to the console providing the full details, inclusive of their respective index number. This is to help facilitate other commands such as pairing or checking.
The implementation of Storage class requires consists of different level of operations:
At the file loading level, it comprises checks to verify the directory is created. This is done by invoking a method:
loadFiles(hasDirectory, hasPropertyFile, hasClientFile, hasPairingFile, clientList, propertyList, pairingList)
.
This method would conduct the following operations:
data
directory if not already exist. (hasDirectory
is false
)hasClientFile
is true
.hasPropertyFile
is true
.hasPairingFile
is true
.An empty file would not be loaded into the ArrayList and PropertyList as the code is designed to read for next()
.
An empty file would invoke a false
in hasNext()
, thus adding operation would not continue. The overall operation can
be visualised in the flowchart above.
When file is appended into the text file, it’s being stored in different formats as shown below:
NAME | CONTACT_NUMBER | EMAIL <optional> | BUDGET
NAME | ADDRESS | RENTAL_PRICE | UNIT_TYPE
[CLIENT_FORMAT] : [PROPERTY_FORMAT]
The text file of which Client, Property and Pairing is being stored is client.txt
, property.txt
and pairing.txt
respectively.
The three sequence diagram above shows the sequence of which the append operation is being invoked. All three
operations are similar in operations but are invoked with different parameter
and path
.
The update operation happens when entries in ClientList and PropertyList is being deleted and entries the hash map of PairingList is being removed.
The sequence diagram of updateClient
, updateProperty
and updatePair
can be seen below:
Note that when delete operation is being invoked on client and property, the updatePair
method will also be invoked to
prevent entries retaining within pairingList after it has been deleted from clientList or propertyList.
There are 3 main steps whenever a list command needs to be executed
list
, ParseManager itself then determines
the type of list command entered, including the tags. ParseListClient, ParseListProperty, ParseListPair and
ParseListEverything are checkers to ensure that a valid command has been entered. ParseListClient and ParseListProperty
also determine if tags have been entered, and if those tags are valid. ParseListPair also checks whether -short
has been added or not.
private Parser parseListCommand(String commandDetail) throws UndefinedSubCommandTypeException {
ArrayList<String> listCommandTypeAndFlags = getListCommandType(commandDetail);
boolean isListProperty = listCommandTypeAndFlags.get(SUB_COMMAND_INDEX).trim().equals(PROPERTY_FLAG);
boolean isListClient = listCommandTypeAndFlags.get(SUB_COMMAND_INDEX).equals(CLIENT_FLAG);
boolean isListEverything = listCommandTypeAndFlags.get(SUB_COMMAND_INDEX).equals(EVERYTHING_FLAG);
boolean isListPairs = listCommandTypeAndFlags.get(SUB_COMMAND_INDEX).equals(PAIR_FLAG);
if (isListProperty) {
return new ParseListProperty(listCommandTypeAndFlags.get(COMMAND_FLAG_INDEX));
} else if (isListClient) {
return new ParseListClient(listCommandTypeAndFlags.get(COMMAND_FLAG_INDEX));
} else if (isListEverything && listCommandTypeAndFlags.get(COMMAND_FLAG_INDEX).isEmpty()) {
return new ParseListEverything();
} else if (isListPairs) {
return new ParseListPair(listCommandTypeAndFlags.get(COMMAND_FLAG_INDEX));
} else {
throw new UndefinedSubCommandTypeException(MESSAGE_INCORRECT_LIST_DETAILS);
}
}
This block of code is part of ParseManager. It determines the type of list operation(-client,
-property, -pair, or -everything) and returns the corresponding object.
Both ParseListClient and ParseListObject then determine if tags are present, and if they are valid,
throwing exceptions if any errors are encountered. They then return the corresponding Command type necessary.
There are seven different classes which handle each of the features described above. Each class inherits from the abstract Command class, and reads information present in either the PropertyList object, ClientList object or both.
ListClientsWithTags
and ListPropertiesWithTags
), it calls the corresponding function.
That function then loops through all the information about clients and properties, and sends a Client or Property
object to the Ui class for printingFor all other cases, the execute function itself runs the loop, which reads every single client, property or pair, and sends individual objects to the Ui class for display.
Property agents who:
Aids property agents in tracking information related to the properties and clients (prospective tenants) they manage. The app enables them to easily:
In addition, the implemented validations will help relieve some burdens experienced by Singapore property agents when dealing with large number of properties. They include the following:
Version | As a … | I want to … | So that I can … |
---|---|---|---|
v1.0 | user | add properties | keep track of properties |
v1.0 | user | add clients | keep track of clients |
v1.0 | user | delete properties | prevent properties I am no longer tracking from cluttering my data |
v1.0 | user | delete clients | prevent clients I am no longer tracking from cluttering my data |
v1.0 | user | view a list of properties | find out what and how many properties I manage |
v1.0 | user | view a list of clients | find out what and how many clients I manage |
v1.0 | user | check the details of a property | view the property’s information |
v1.0 | user | pair a client to a property | record down which client is renting which property |
v1.0 | user | unpair a client to a property | update my rental records when a client is no longer renting property |
v1.0 | user | save my data | used the data created from a previous use of the app |
v1.0 | user | quit the app | free up memory for other applications |
v2.0 | user | check the details of a client | view the client’s information |
v2.0 | user | search clients using their details | easily find specific clients |
v2.0 | user | search properties using their details | easily find specific properties |
v2.0 | user | list only specific details about clients and properties | Display the information I need without cluttering the screen |
v2.1 | user | view a list of pairings completed | Keep track of all pairings I have already made |
11
or above installed.java -jar PropertyRentalManager.jar
Add client
6
, 8
or 9
followed by 7 digits.add -client n/Gary Oaks c/90876543 e/garyoaks@example.com b/1550
6
, 8
or 9
followed by 7 digits.add -client n/Gary Oaks c/90876543 b/1550
6
, 8
or 9
followed by 7 digits.add -client n/Gary Oaks c/10876543 e/garyoaks@example.com b/1550
@
symbol.add -client n/Gary Oaks c/90876543 e/garyoaksexample.com b/1550
add -client n/Gary Oaks c/90876543 e/garyoaks@example.com b/0
Test cases:
add -client n/Gary Oaks c/90876543 e/garyoaks@example.com b/1550
add -client n/Gary Oaks c/60876543 e/garyoaks@example.com b/1550
Expected: Terminal shows duplicating client error message along with the details of existing client.
add -client
e/
) are included in the right order.add -client n/ c/90876543 e/garyoaks@example.com b/
e/
) is missing.add -client n/Gary Oaks c/90876543 e/garyoaks@example.com 1550
e/
) are present.add -client b/Gary Oaks c/90876543 e/garyoaks@example.com n/1550
Add property
LP
. LP
implies landed property unit type.add -property n/Ash Ketchun a/25A Pallet Town, S121111 p/1600 t/LP BGL
LP
. LP
implies landed property unit type.add -property n/Ash Ketchun a/101 Marlow Street #12-05, S059020 p/1600 t/HDB 3
add -property n/Ash Ketchun a/idk whats my address p/1600 t/LP BGL
LP
. LP
implies landed property unit type.add -property n/Ash Ketchun a/101 Marlow Street #12-05, S059020 p/00 t/HDB 3
add -property n/Ash Ketchun a/101 Marlow Street #12-05, S059020 p/1600 t/hi
add -property n/Ash Ketchun a/101 Marlow Street, S059020 p/1600 t/HDB 3
Test cases:
add -property n/Ash Ketchun a/25A Pallet Town, S121111 p/1600 t/LP BGL
add -property n/Joe a/25A pAlLeT ToWn, S121111 p/1600 t/LP BGL
Expected: Terminal shows duplicating property error message along with details of existing property.
add -property
add -property n/Ash Ketchun a/25A Pallet Town, S121111 p/ t/LP BGL
add -property Ash Ketchun a/25A Pallet Town, S121111 p/1600 t/LP BGL
add -property a/Ash Ketchun n/25A Pallet Town, S121111 p/1600 t/LP BGL
Test case: delete -client i/1
Expected: Terminal shows a successful client deletion message and the deleted client’s details.
Test case: delete -property i/1
Expected: Terminal shows a successful property deletion message and the deleted property’s details.
Test case: delete -client
Expected: Terminal shows error message.
Test case: delete -client i/[INDEX]
where INDEX is an index that is not in the client list (1-indexed)
Expected: Terminal shows error message.
Test case: delete -client i/hi
Expected: Terminal shows error message.
Test case: delete -property
Expected: Terminal shows error message.
Test case: delete -property i/[INDEX]
where INDEX is an index that is not in the property list (1-indexed)
Expected: Terminal shows error message.
Test case: delete -property i/hi
Expected: Terminal shows error message.
list -client
It also lists the number of clients present at the time, after listing them all. The format for that is as follows -
There are 2 clients in this list```list -client n/
to demonstrate1.Doja Cat
list -client -short
It also lists the number of clients present in the list in the end.
All of these display the following when no clients are present in the list -
There are 0 clients in this list```list -property
This also lists the number of properties present in the list at the end. The format for
that is as follows -
There is 1 property in this list```We will use list -property a/
to demonstrate1. 25 Lower Kent Ridge Rd, S119081
It also lists the total number of properties present in the listlist -property -short
There are 0 properties in this list
list -pair
Client:
Client Name: Doja Cat
Client Contact Number: 93437878
Client Email: doja88@example.com
Client Budget: 2000
Property:
Landlord Name: Bob Tan Bee Bee
Property Address: 25 Lower Kent Ridge Rd, S119081
Property Rental Price: 1000
Unit Type: LP Bungalow
It also lists the number of pairs present in the list. The format is as follows -
There is 1 pair in this list
list -pair -short
Expected: Lists the short details of both clients and properties described earlier, for every
pair in the list. The format is as follows -
Client:
Client Name: Doja Cat
Client Budget: 2000
Property:
Property Address: 25 Lower Kent Ridge Rd, S119081
Unit Type: LP Bungalow
Property Rental Price: 1000
It also lists the total number of pairs present in the list.
list -everything
Properties:
Pairs: Client: Client Name: Doja Cat Client Contact Number: 93437878 Client Email: doja88@example.com Client Budget: 2000 Property: Landlord Name: Bob Tan Bee Bee Property Address: 25 Lower Kent Ridge Rd, S119081 Property Rental Price: 1000 Unit Type: LP Bungalow ——————————————————————————– There is 1 pair in this list ```
Test case: pair ip/1 ic/1
Expected: Pairing is added. Terminal shows successful pairing message, the name of the paired client, and the address of the paired property.
Test case: pair ip/1 ic/1
Expected: Terminal shows unsuccessful pairing message, name and budget of client, and the address and rental price of the property.
pair ip/1 ic/1
Test case: pair ip/2 ic/1
(pair a different property to the same client)
Expected: Terminal shows unsuccessful pairing message.
pair ip/1 ic/1
Test case: pair ip/1 ic/1
(re-pair using the same indexes)
Expected: Terminal shows unsuccessful pairing message.
pair ip/1 ic/1
Test case: unpair ip/1 ic/1
(unpair using the same indexes as the pair command)
Expected: Pairing is deleted. Terminal shows successful unpairing message with the client’s name and the property address.
Test case: unpair ip/1 ic/1
Expected: Terminal shows unsuccessful unpairing message.
pair ip/1 ic/1
and pair ip/1 ic/2
Test case: check -client i/1
Expected: Terminal shows details of the client and information of the property this client is renting. Number of list results is greater than 0.
Test case: check -client i/2
Expected: Terminal shows details of the client and a message showing that this client is not renting any property.
Test case: check -client i/0
Expected: Terminal shows error message.
Test case: check -client i/[INDEX]
, where INDEX is an index that is not in the client list (1-indexed).
Expected: Terminal shows error message.
Test case: check -client i/hello
Expected: Terminal shows error message.
pair ip/1 ic/1
and pair ip/1 ic/2
Test case: check -property i/1
Expected: Terminal shows details of the property and information of the clients renting the property. Number of list results is greater than 0.
Test case: check -property i/2
Expected: Terminal shows details of the property, number of list results is 0.
Test case: check -property i/0
Expected: Terminal shows error message.
Test case: check -property i/[INDEX]
, where INDEX is an index that is not in the property list (1-indexed).
Expected: Terminal shows error message.
Test case: check -property i/1r2342
Expected: Terminal shows error message.
Querying for Client/Property stored in the Client/Property List:
list -client
command to verify that there is at least 1 client in the database.find -client f/<QUERY_TEXT>
<QUERY_TEXT>
portion. Omit the directional brackets when entering query text.find -client f/<QUERY_TEXT>
find -client n/ f/
f/
find -client
find -property f/<QUERY_TEXT>
<QUERY_TEXT>
portion. Omit the directional brackets when entering the query text.find -property f/<NON_EXISTENT_QUERY_TEXT>
find -property t/3-Room f/
f/
.find -property
f/
tag will not be flagged as an error since it’s possible that names contains a forward slash (/).Querying for Client/Property stored in the Client/Property List:
list -client
command to verify that there is at least 1 client in the database.find -client f/<QUERY_TEXT>
<QUERY_TEXT>
portion. Omit the directional brackets when entering query text.find -client f/<QUERY_TEXT>
find -client n/ f/
f/
find -client
find -property f/<QUERY_TEXT>
<QUERY_TEXT>
portion. Omit the directional brackets when entering the query text.find -property f/<NON_EXISTENT_QUERY_TEXT>
find -property t/3-Room f/
f/
.find -property
f/
tag will not be flagged as an error since it’s possible that names contains a forward slash (/).