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 -
Note: The C symbols are a result of the PlantUml layout.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> | BUDGETNAME | 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.
The above is an example for CommandListClients. It reads from ClientList. Then, it displays each line
using the displayOneClient function in Ui. Note that the C tags for class are a result of the PlantUml
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/15506, 8 or 9 followed by 7 digits.add -client n/Gary Oaks c/90876543 b/15506, 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/1550add -client n/Gary Oaks c/90876543 e/garyoaks@example.com b/0Test 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 -cliente/) 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 1550e/) are present.add -client b/Gary Oaks c/90876543 e/garyoaks@example.com n/1550Add property
LP. LP implies landed property unit type.add -property n/Ash Ketchun a/25A Pallet Town, S121111 p/1600 t/LP BGLLP. LP implies landed property unit type.add -property n/Ash Ketchun a/101 Marlow Street #12-05, S059020 p/1600 t/HDB 3add -property n/Ash Ketchun a/idk whats my address p/1600 t/LP BGLLP. LP implies landed property unit type.add -property n/Ash Ketchun a/101 Marlow Street #12-05, S059020 p/00 t/HDB 3add -property n/Ash Ketchun a/101 Marlow Street #12-05, S059020 p/1600 t/hiadd -property n/Ash Ketchun a/101 Marlow Street, S059020 p/1600 t/HDB 3Test 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 -propertyadd -property n/Ash Ketchun a/25A Pallet Town, S121111 p/ t/LP BGLadd -property Ash Ketchun a/25A Pallet Town, S121111 p/1600 t/LP BGLadd -property a/Ash Ketchun n/25A Pallet Town, S121111 p/1600 t/LP BGLTest 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 Catlist -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 -shortThere are 0 properties in this listlist -pairClient:
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 -everythingProperties:
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/1Test case: pair ip/2 ic/1 (pair a different property to the same client)
Expected: Terminal shows unsuccessful pairing message.
pair ip/1 ic/1Test case: pair ip/1 ic/1 (re-pair using the same indexes)
Expected: Terminal shows unsuccessful pairing message.
pair ip/1 ic/1Test 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/2Test 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/2Test 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 (/).