Library Management System - Book Management
Implement book status management using enums and switch expressions
Objectives
By the end of this practical work, you will be able to:
- Create and use enums with values and methods
- Implement decision logic using if/else and switch statements
- Use modern switch expressions with arrow syntax
- Create a console menu system for user interaction
- Debug code using IntelliJ IDEA debugger
Prerequisites
- Completed Practical Work 3 (Library System Foundation)
- Book, Member, and BookCategory classes from PW3
- Understanding of basic Java syntax
Part 1: Enhanced BookStatus Enum
Step 1.1: Create BookStatus Enum with Values
Replace the simple available boolean with a rich enum:
package com.library.model;
public enum BookStatus {
AVAILABLE("Available for borrowing", true), // (#1:Enum constant with values)
BORROWED("Currently on loan", false),
RESERVED("Reserved by a member", false),
DAMAGED("Damaged - under repair", false),
LOST("Reported lost", false),
REFERENCE_ONLY("Cannot be borrowed", false); // (#2:Last constant ends with semicolon)
private final String description; // (#3:Enum fields)
private final boolean canBeBorrowed;
BookStatus(String description, boolean canBeBorrowed) { // (#4:Enum constructor)
this.description = description;
this.canBeBorrowed = canBeBorrowed;
}
public String getDescription() {
return description;
}
public boolean canBeBorrowed() {
return canBeBorrowed;
}
public String getDisplayText() { // (#5:Custom method)
return name() + " - " + description;
}
}
Step 1.2: Update Book Class
Modify the Book class to use BookStatus:
// Replace the 'available' boolean field with:
private BookStatus status;
// Update constructor
public Book(String isbn, String title, String author, int publicationYear, BookCategory category) {
this.isbn = isbn;
this.title = title;
this.author = author;
this.publicationYear = publicationYear;
this.category = category;
this.status = BookStatus.AVAILABLE; // Default status
}
// Replace isAvailable() with:
public BookStatus getStatus() {
return status;
}
public void setStatus(BookStatus status) {
this.status = status;
}
public boolean canBeBorrowed() {
return status.canBeBorrowed();
}
// Update displayInfo()
public void displayInfo() {
System.out.println("========== Book Info ==========");
System.out.println("ISBN: " + isbn);
System.out.println("Title: " + title);
System.out.println("Author: " + author);
System.out.println("Year: " + publicationYear);
System.out.println("Category: " + category);
System.out.println("Status: " + status.getDisplayText());
System.out.println("===============================");
}
Part 2: Switch Expressions
Step 2.1: Status-Based Actions with Switch Expression
Create a method that returns the appropriate action based on book status:
// Add to Book class
public String getAvailableAction() {
return switch (status) { // (#1:Switch expression)
case AVAILABLE -> "Borrow this book"; // (#2:Arrow syntax - no break needed)
case BORROWED -> "Join waiting list";
case RESERVED -> "This book is reserved";
case DAMAGED -> "Book under repair - check back later";
case LOST -> "Book is lost - consider alternatives";
case REFERENCE_ONLY -> "Read in library only";
}; // (#3:Switch expression returns a value, ends with semicolon)
}
Step 2.2: Calculate Late Fee with Switch
Create a utility class for fee calculations:
package com.library.service;
import com.library.model.BookCategory;
public class FeeCalculator {
// Late fee depends on book category
public static double calculateLateFee(BookCategory category, int daysLate) {
if (daysLate <= 0) return 0.0;
double dailyRate = switch (category) {
case FICTION, CHILDREN -> 0.25; // (#1:Multiple cases with comma)
case NON_FICTION, BIOGRAPHY, HISTORY -> 0.50;
case SCIENCE, TECHNOLOGY -> 0.75; // (#2:Higher value items)
};
double fee = dailyRate * daysLate;
return Math.min(fee, 25.0); // Cap at 25 euros
}
// Borrow duration depends on category
public static int getBorrowDuration(BookCategory category) {
return switch (category) {
case FICTION, NON_FICTION, BIOGRAPHY -> 21; // 3 weeks
case SCIENCE, TECHNOLOGY -> 14; // 2 weeks - high demand
case HISTORY -> 28; // 4 weeks
case CHILDREN -> 14; // 2 weeks
};
}
}
Step 2.3: Switch with yield for Complex Logic
Use yield when you need multiple statements:
public String getStatusMessage() {
return switch (status) {
case AVAILABLE -> "Ready to borrow!";
case BORROWED -> { // (#1:Block for complex logic)
System.out.println("Checking return date...");
yield "Currently on loan"; // (#2:yield returns the value)
}
case RESERVED -> {
System.out.println("Notifying reservation holder...");
yield "Reserved - waiting for pickup";
}
default -> status.getDescription(); // (#3:Default case)
};
}
Part 3: Console Menu System
Step 3.1: Create LibraryConsole Class
Create an interactive menu:
package com.library;
import com.library.model.*;
import com.library.service.FeeCalculator;
import java.util.Scanner;
public class LibraryConsole {
private Scanner scanner;
private Book[] books;
private int bookCount;
public LibraryConsole() {
scanner = new Scanner(System.in);
books = new Book[100]; // Simple array for now
bookCount = 0;
initializeSampleBooks();
}
private void initializeSampleBooks() {
books[bookCount++] = new Book("978-0-13-468599-1", "Effective Java",
"Joshua Bloch", 2018, BookCategory.TECHNOLOGY);
books[bookCount++] = new Book("978-0-06-112008-4", "To Kill a Mockingbird",
"Harper Lee", 1960, BookCategory.FICTION);
books[bookCount++] = new Book("978-0-7432-7356-5", "1776",
"David McCullough", 2005, BookCategory.HISTORY);
}
public void run() {
boolean running = true;
while (running) {
displayMenu();
int choice = getIntInput("Enter your choice: ");
switch (choice) {
case 1 -> listAllBooks();
case 2 -> searchBook();
case 3 -> addNewBook();
case 4 -> changeBookStatus();
case 5 -> calculateFee();
case 0 -> {
running = false;
System.out.println("Goodbye!");
}
default -> System.out.println("Invalid choice. Please try again.");
}
}
scanner.close();
}
private void displayMenu() {
System.out.println("\n====== Library Management System ======");
System.out.println("1. List all books");
System.out.println("2. Search for a book");
System.out.println("3. Add a new book");
System.out.println("4. Change book status");
System.out.println("5. Calculate late fee");
System.out.println("0. Exit");
System.out.println("========================================");
}
private int getIntInput(String prompt) {
System.out.print(prompt);
while (!scanner.hasNextInt()) {
System.out.println("Please enter a valid number.");
scanner.next();
System.out.print(prompt);
}
int value = scanner.nextInt();
scanner.nextLine(); // Consume newline
return value;
}
private String getStringInput(String prompt) {
System.out.print(prompt);
return scanner.nextLine();
}
private void listAllBooks() {
System.out.println("\n--- All Books ---");
if (bookCount == 0) {
System.out.println("No books in the library.");
return;
}
for (int i = 0; i < bookCount; i++) {
System.out.println((i + 1) + ". " + books[i].getTitle() +
" [" + books[i].getStatus() + "]");
}
}
private void searchBook() {
String searchTerm = getStringInput("Enter search term (title or author): ");
System.out.println("\n--- Search Results ---");
boolean found = false;
for (int i = 0; i < bookCount; i++) {
if (books[i].getTitle().toLowerCase().contains(searchTerm.toLowerCase()) ||
books[i].getAuthor().toLowerCase().contains(searchTerm.toLowerCase())) {
books[i].displayInfo();
found = true;
}
}
if (!found) {
System.out.println("No books found matching: " + searchTerm);
}
}
private void addNewBook() {
System.out.println("\n--- Add New Book ---");
String isbn = getStringInput("ISBN: ");
String title = getStringInput("Title: ");
String author = getStringInput("Author: ");
int year = getIntInput("Publication Year: ");
System.out.println("Categories: ");
BookCategory[] categories = BookCategory.values();
for (int i = 0; i < categories.length; i++) {
System.out.println((i + 1) + ". " + categories[i]);
}
int catChoice = getIntInput("Choose category (1-" + categories.length + "): ");
if (catChoice >= 1 && catChoice <= categories.length) {
books[bookCount++] = new Book(isbn, title, author, year,
categories[catChoice - 1]);
System.out.println("Book added successfully!");
} else {
System.out.println("Invalid category choice.");
}
}
private void changeBookStatus() {
listAllBooks();
int bookChoice = getIntInput("Select book number: ");
if (bookChoice < 1 || bookChoice > bookCount) {
System.out.println("Invalid selection.");
return;
}
Book book = books[bookChoice - 1];
System.out.println("Current status: " + book.getStatus().getDisplayText());
System.out.println("\nAvailable statuses:");
BookStatus[] statuses = BookStatus.values();
for (int i = 0; i < statuses.length; i++) {
System.out.println((i + 1) + ". " + statuses[i].getDisplayText());
}
int statusChoice = getIntInput("Choose new status: ");
if (statusChoice >= 1 && statusChoice <= statuses.length) {
book.setStatus(statuses[statusChoice - 1]);
System.out.println("Status updated to: " + book.getStatus());
}
}
private void calculateFee() {
listAllBooks();
int bookChoice = getIntInput("Select book number: ");
if (bookChoice < 1 || bookChoice > bookCount) {
System.out.println("Invalid selection.");
return;
}
Book book = books[bookChoice - 1];
int daysLate = getIntInput("Days late: ");
double fee = FeeCalculator.calculateLateFee(book.getCategory(), daysLate);
System.out.printf("Late fee for '%s': %.2f euros%n", book.getTitle(), fee);
}
}
Step 3.2: Update Main Class
Update Main.java to run the console:
package com.library;
public class Main {
public static void main(String[] args) {
LibraryConsole console = new LibraryConsole();
console.run();
}
}
Part 4: Debugging Exercise
Step 4.1: Buggy Code to Debug
The following method has bugs. Use the debugger to find and fix them:
// Add to FeeCalculator class
public static String getMembershipDiscount(int yearsAsMember, double fee) {
double discount;
if (yearsAsMember > 5) {
discount = 0.20; // 20% discount
} else if (yearsAsMember > 2) {
discount = 0.10; // 10% discount
} else if (yearsAsMember >= 1) {
discount = 0.05; // 5% discount
}
// BUG 1: What if yearsAsMember < 1?
double discountedFee = fee - (fee * discount);
// BUG 2: discount might not be initialized!
if (discountedFee < 0) {
discountedFee = 0; // This is correct
}
return String.format("Original: %.2f, Discount: %.0f%%, Final: %.2f",
fee, discount * 100, discountedFee);
}
Your Task: Set breakpoints and step through the code to find the bugs. Fix them!
Step 4.2: Debug Instructions
- Click in the gutter (left margin) next to line numbers to set breakpoints
- Right-click on Main and select Debug 'Main'
- Use the debug controls:
- F8 - Step Over (next line)
- F7 - Step Into (enter method)
- Shift+F8 - Step Out (exit method)
- F9 - Resume (continue to next breakpoint)
- Watch the Variables panel to see values
- Use Evaluate Expression (Alt+F8) to test fixes
Exercises
Exercise 1: Member Type Enum
Create a MemberType enum with different membership levels:
- BASIC - 5 books max, no renewal
- STANDARD - 10 books max, 1 renewal
- PREMIUM - 20 books max, unlimited renewals
- STUDENT - 15 books max, 2 renewals, 50% off fees
Exercise 2: Book Availability Check
Implement a method that uses switch expression to return availability message:
public String checkAvailability(BookStatus status, int waitlistSize) {
// Return appropriate message based on status and waitlist
// AVAILABLE + waitlist=0 -> "Available now!"
// AVAILABLE + waitlist>0 -> "Available, but X people waiting"
// BORROWED -> "Due back in X days" (assume 14 days)
// etc.
}
Exercise 3: Fix the Bugs
Debug and fix the getMembershipDiscount method. Submit:
- Screenshot of your debugging session
- The fixed code
- Explanation of each bug found
Exercise 4: Enhanced Menu
Add these features to the console menu:
- Filter books by status (show only available, only borrowed, etc.)
- Filter books by category
- Sort books by title or year
Deliverables
- Source Code: Updated project with enums and menu system
- Debug Report: Screenshot + explanation of bugs found
- Console Output: Screenshots showing menu interactions
Bonus Challenges
- Challenge 1: Add pattern matching in switch (Java 21) to handle different input types
- Challenge 2: Create a ReservationStatus enum with transitions (PENDING -> CONFIRMED -> FULFILLED)
- Challenge 3: Implement input validation with custom error messages