The Reservation System application is designed to facilitate the reservation and management of tables in a dining establishment. It aims to simplify the process of making reservations, ensure efficient utilization of available tables, and manage reservation-related tasks. Moreover is also possible to add a food list for each reservation.
Each reservation in the app has a fixed time limit of 2 hours. Reservations cannot overlap within this 2-hour time frame. Manual deletion of reservations is required to free up time slots.
Click the "Select Dishes" button to choose dishes for a selected reservation. Please note that this action is only available when a single reservation is selected.
Use the "Show Paid Dishes" button to view the dishes that require payment. This action is available for each reservation.
To delete a reservation, locate the "Delete" button. Click this button to remove the selected reservation. Works for each reservation selected.
You can clean all reservations at once by using the "Clean All Reservations" button. This action clears all reservations from the app.
To open the calendar, just press the button "Select Day", and you can select the day to add reservations. The day selected will become green and after one second the calendar will be closed. I choose to put a colour when the button is pressed to show the ResturantManager the day pressed.
This application is composed by three packages:
We are using Java 17.0.1. hence the maven compiler sources and target, in the .pom file, are 17 and 17:
<maven.compiler.source> 17 </maven.compiler.source>
<maven.compiler.target> 17 </maven.compiler.target>
Steps:
Normal Classes:
mvn javadoc:javadoc:
Test:
mvn javadoc:test-javadoc
run such command in the root folder of the project or you will get errors.
Note : after executing such commands the javadoc documentation is findable online to the following path :
I have assumed to be a Restaurant waiter. My restaurant is very small. In my restaurant there are in total 10 tables . The first five tables are designed for people , whereas the last five tables are designed for people with special needs. On the one hand, the first 5 tables have 5 sitting places for each table. On the other hand, the last 5 tables have 3 sitting place for table. We all wish to make all people sit . So I thought this way :
one table designed for people with Special Needs composed by 3 of such people of the last group + another table composed by the other 2 people with disabilities and another person ( another person : that can be either another person with disabilities or a person).
For a group of 4 people and 1 person with disabilities, they will be seated at table 1 (Case 1).
For a group of 1 person and 4 people with disabilities, they will be seated at table 3 (Special Case).
For a group of 1 person and 2 people with disabilities, they will be seated at table 7 (Case 2).
By following this seating arrangement logic, we aim to provide an inclusive and comfortable dining experience for all customers.
Overlapping Reservation Warning (showOverlapAlert):
This warning is triggered when a new reservation overlaps with an existing one. Overlapping reservations can lead to seating conflicts and customer dissatisfaction. The app considers by default leaving time of each booking as: arrivalTime + 2 hours.
Invalid Input Warning (showInvalidNumberAlert, showInvalidInputAlert):
These warnings are shown when the waiter enters invalid input values, such as non-numeric characters or negative values. Handling invalid inputs prevents unexpected behavior in the application and guides the waiter to enter correct and meaningful data.
Table Already Reserved Warning (showReservedAlert):
This warning informs the waiter that the selected table is already reserved during the chosen time slot. It helps prevent double bookings and ensures that each table is only reserved once at a given time.
Reservation Category and Capacity Mismatch Warning (validateTableCategory):
This warning addresses the scenario where the calculated category or capacity of the table doesn't match the selected values. Ensuring that the table's attributes align with the reservation group's characteristics avoids assigning customers to inappropriate tables. (ensuring that max 5 or 3 people can sit to the respective tables, and in case of minor capacity that the default one, the capacity must be = total people).
Unsuccessful Table Selection Warning (validateTableNumber):
This warning is displayed if the waiter selects a table number that is not available for reservations. It guides the waiter to choose a table with a valid number within the specified range.
Serialization Error Handling (serializeJsonFile, cleanJson):
These methods handle the serialization of reservations into a JSON file. Serialization errors could lead to data loss or corruption. By handling these errors and saving data consistently, you ensure that reservations are properly stored and retrieved.
Table Unlocking (unlockTransition):
This mechanism ensures that tables are automatically unlocked after a reservation's departure time. By using a PauseTransition, the system make sure that tables become available for new reservations after the specified time has passed.
I faced an issue while developing the restaurant reservation system where food orders for one reservation were unintentionally affecting other reservations. The problem was initially attributed to data sharing among reservations. There was a shared reference to the same name variable for all iterations of the loop, causing unexpected behavior in the assignment of food items to reservations. I successfully attempted a solution by using a mapping (dictionary) to store food orders separately for each reservation to store food orders independently.
The bug is that one reservation or more keep staying and the food list cannot be seen. So to see the code working properly you must select the reservation and only than select the food, otherwise is the system don't see the selection there is such bug.
FIX: the problem was in the method firma. We need also the reservation display in this way:
showOrderedFoodDialog(reservation,reservationDisplay);
FIX: uploaded both the method " openDishesPopUp(Reservation reservation) " in this way:
...
// Append the new plates and quantities to the existing selectedPlatesMap
String plateName = plate.getName();
int existingQuantity = selectedPlatesMap.getOrDefault(plateName, 0);
selectedPlatesMap.put(plateName, existingQuantity + quantity);
}
}
// Update the reservation with the updated selectedPlatesMap
reservation.setPlatesMap(selectedPlatesMap);
}
...
as well as modified this in the constructor of Reservation class:
this.platesMap = new HashMap<>();
before I had:
this.platesMap = platesMap;
which didn't initialise anything.
...
// Stage.class:
public void showAndWait() {
Toolkit.getToolkit().checkFxUserThread();
if (this.isPrimary()) {
throw new IllegalStateException("Cannot call this method on primary stage");
} else if (this.isShowing()) {
throw new IllegalStateException("Stage already visible"); // this was my warning on the terminal !
} else if (!Toolkit.getToolkit().canStartNestedEventLoop()) {
throw new IllegalStateException("showAndWait is not allowed during animation or layout processing");
} else {
assert !this.inNestedEventLoop;
this.show();
this.inNestedEventLoop = true;
Toolkit.getToolkit().enterNestedEventLoop(this);
}
}
I have tried different options but in the end I suppose I would need to change the structure of the majority of classes to achieve what I want. My final idea is to have an hashmap of reservations that need to be serialized and deserialized. And so I want to use an hashmap like this one :
The Map<LocalDate, List
private Map<LocalDate, List
In this way the json would have a different json structure, after the deserialization of hashmap data into 1 json-file :
like this :
{
"2024-01-14": [
{
"tableId": 1,
"customerId": "cust123",
"startTime": "18:00",
"endTime": "20:00"
},
{
"tableId": 2,
"customerId": "cust456",
"startTime": "19:00",
"endTime": "21:00"
}
],
"2024-01-15": [
// Reservations for 2024-01-15
]
}
In this way we could prevent data to be lose when we change day in the calendar.
Example of a modified restructure of some class I started doing but required many other change as well as tests :
public class Main {
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
Map<LocalDate, List<Reservation>> reservationsMap = new HashMap<>();
// Create a new back.Table instance
Table table = new Table(3, 4);
try {
objectMapper.writeValue(new File("src/main/resources/tables.json"), reservationsMap);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ReservationSystem {
private final Map<LocalDate, List<Reservation>> tables;
private Reservation reservation;
//to convert data enclosed into a Map into a String. To make it work is it also necessary of a dependence in the .pom file
objectMapper.registerModule(new JavaTimeModule());
/**
* Creates a new instance of the `ReservationSystem` class.
* Initializes the list of reservations and reads table data from a JSON file.
*/
public ReservationSystem() {
tables = readReservationDataFromJson("src/main/resources/tables.json");
}
public boolean isTableAvailable(int tableNumber, LocalDate date) {
if (tables != null) {
List<Reservation> reservationsForDate = tables.get(date);
if (reservationsForDate != null) {
for (Reservation reservation : reservationsForDate) {
if (reservation.getTableNumber() == tableNumber && !reservation.isAvailable()) {
return false; // Table is reserved on this date
}
}
}
}
return true; // Table is available if not found in reservations or no reservations for this date
}
/**
* Reads table data from a JSON file and initializes table availability.
*
* @return A list of table reservations.
*/
private Map<LocalDate, List<Reservation>> readReservationDataFromJson(String filePath) {
ObjectMapper objectMapper = new ObjectMapper();
try {
TypeReference<HashMap<LocalDate, List<Reservation>>> typeRef
= new TypeReference<HashMap<LocalDate, List<Reservation>>>() {
};
return objectMapper.readValue(new File(filePath), typeRef);
} catch (IOException e) {
e.printStackTrace();
return new HashMap<>();
}
public void validateAllBookingTimes() {
for (Map.Entry<LocalDate, List<Reservation>> entry : tables.entrySet()) {
for (Reservation reservation : entry.getValue()) {
validateTableBookingTime(reservation.getStartTime());
validateTableBookingTime(reservation.getEndTime());
}
}
}
private static void validateTableBookingTime(String time){
if (!time.matches("^([01]?[0-9]|2[0-3]):[0-5][0-9]$")) {
throw new IllegalArgumentException("Invalid table booking time: " + time);
}
}
/**
* Calculates and updates the category of each reservation in the hash map.
*/
public void calculateCategoriesForAllReservations() {
for (List<Reservation> reservations : tables.values()) {
for (Reservation reservation : reservations) {
String category = calculateCategoryForReservation(reservation);
// Assuming Reservation has a method to set its category
reservation.setCategory();
}
}
}
}
private String calculateCategoryForReservation(Reservation reservation) {
int normalPeople = reservation.normalPeople;
int disabilitiesPeople = reservation.disabilitiesPeople;
if (disabilitiesPeople >= normalPeople) {
return "Special Needs";
} else {
return "Normal";
}
}
}
What I may add also to keep mainMerged and Calendar keeping being syncronized is using an interface that notifies MainMerged everytime the button of another day is pressed :
For example:
// In ReservationCalendar:
public interface DateSelectionListener {
void onDateSelected(LocalDate date);
}
// In MainMerged
@Override
public void onDateSelected(LocalDate date) {
this.selectedDate = date;
updateReservationDisplay(); // Update the list of reservations
}
I finally solved it, although there is a little bug that for whichever day I select, for each month, the selected day is always the last of the month. I changed a lot of datastructures in classes, as I serialize and deserialize Hashmaps of date and reservations. So now the json has both dates and inside the reservation of each date!
Note : I will upload this final version in a zip file called "ReservationSystemMirkoHashMapWorking" inside this project in the root folder, because I haven't done tests and there is still a small bug to solve ( the previous one). So you can also compare better all the structure changed. To see such last version just extract the archive and open as a normal project. From Intelliji go to file/open and select the project, that is called "ReservationSystemMirko". Note : extract such zip in a separate folder tahn the cloned repo, since the folder has the same name ! Otherwise you will overwrite it.
Main changes done:
Custom serializer class:
Introduced a custom serializer class (CustomSerializersModule) to handle the serialization of LocalDate objects. This class ensures precise and efficient serialization of dates, improving data integrity and storage.
Custom deserializer class:
Used a custom deserializer class (LocalDateDeserializer) to handle the deserialization of LocalDate objects. This class ensures proper parsing and interpretation of dates during deserialization, maintaining data consistency.
Structured reservations in a map:
Employed a Map<LocalDate, List
Adapted the ReservationSystem class to utilize the new Map<LocalDate, List
Incorporated dependencies to handle serialization and deserialization of hashmaps, enabling efficient data handling. This ensures smooth conversion between data structures and simplifies data management.
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.9.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
I utilize JavaFX's Observable List to manage and display reservations in a table view, ensuring smooth interaction and data management.
I have implemented custom exceptions in my code, including IllegalArgumentException
for input validation, and a custom exception called ReservationSystemException
for specific reservation-related errors.
To ensure robust error handling, I employ try-catch blocks for handling exceptions in my code, enhancing the application's stability.
I leverage method overloading in various event handlers, such as reserveButton.setOnAction(...)
, and showInvalidInputAlert(...)
, allowing for flexibility in handling different types of inputs.
I use lambda expressions for event handling within my code. For instance, in reserveButton.setOnAction(e -> { ... })
, lambda expressions enhance the readability and conciseness of the code.
My application incorporates file I/O operations to read and write reservation data to and from a JSON file, ensuring persistent data storage.
For JSON serialization, I rely on Jackson's ObjectMapper, enabling me to convert objects to JSON format for storage and transmission purposes.
I use Jackson's ObjectMapper for JSON deserialization, allowing me to parse JSON data back into objects within my application, ensuring seamless data retrieval.
In my code, I incorporate test hooks for various testing purposes, including setup and teardown procedures, facilitating efficient and comprehensive testing.
My application features a JavaFX graphical user interface, providing an interactive platform for users to interact with the system, enhancing user experience.
I employ regular expressions to split and validate time (hours/minutes) input, ensuring accurate and reliable data processing.
I have implemented a new calendar in the "back" package, accessible by setting the source in the .pom file to "back/ReservationCalendar" at line 25 within the
Coding this reservation system was an enlightening journey. I have particularly enjoyed and developed serialization and deserialization skills. Additionally, my testing abilities, which I have improved following a testing specific course at university, received a significant boost. This project gave me the opportunity to put in practice what I have learnt in such course.