PrintedBook.java
package com.university.bookstore.model;
import java.util.regex.Pattern;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Represents a physical printed book in the bookstore inventory.
* Extends Material class to demonstrate inheritance.
*
* <p>This class maintains backward compatibility with the original Book class
* while integrating into the new polymorphic material hierarchy.</p>
*
* @author Navid Mohaghegh
* @version 2.0
* @since 2024-09-15
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class PrintedBook extends Material {
private static final Pattern ISBN_13_PATTERN = Pattern.compile("^\\d{13}$");
private static final Pattern ISBN_10_PATTERN = Pattern.compile("^\\d{9}[\\dX]$");
private final String isbn;
private final String author;
private final int pages;
private final String publisher;
private final boolean hardcover;
/**
* Creates a new PrintedBook with validation.
*
* @param isbn the International Standard Book Number
* @param title the book title
* @param author the primary author
* @param price the price in dollars
* @param year the publication year
* @param pages number of pages
* @param publisher the publishing company
* @param hardcover true if hardcover, false if paperback
*/
@JsonCreator
public PrintedBook(@JsonProperty("id") String isbn,
@JsonProperty("title") String title,
@JsonProperty("author") String author,
@JsonProperty("price") double price,
@JsonProperty("year") int year,
@JsonProperty("pages") int pages,
@JsonProperty("publisher") String publisher,
@JsonProperty("hardcover") boolean hardcover) {
super(validateIsbn(isbn), title, price, year, MaterialType.BOOK);
this.isbn = this.id;
this.author = validateStringField(author, "Author");
this.pages = validatePages(pages);
this.publisher = validateStringField(publisher, "Publisher");
this.hardcover = hardcover;
}
/**
* Convenience constructor for backward compatibility.
*/
public PrintedBook(String isbn, String title, String author, double price, int year) {
this(isbn, title, author, price, year, 0, "Unknown", false);
}
private static String validateIsbn(String isbn) {
if (isbn == null) {
throw new NullPointerException("ISBN cannot be null");
}
String cleaned = isbn.replaceAll("-", "").trim();
if (!ISBN_10_PATTERN.matcher(cleaned).matches() &&
!ISBN_13_PATTERN.matcher(cleaned).matches()) {
throw new IllegalArgumentException(
"ISBN must be 10 or 13 digits. Provided: " + isbn);
}
return cleaned;
}
private int validatePages(int pages) {
if (pages < 0) {
throw new IllegalArgumentException("Pages cannot be negative");
}
return pages;
}
@Override
public String getCreator() {
return author;
}
@Override
public String getDisplayInfo() {
return String.format("%s by %s (%d, %s) - %d pages, $%.2f",
title, author, year,
hardcover ? "Hardcover" : "Paperback",
pages, price);
}
@Override
public double getDiscountRate() {
int currentYear = java.time.Year.now().getValue();
if (year < currentYear - 2) {
return 0.15;
}
return 0.0;
}
public String getIsbn() {
return isbn;
}
public String getAuthor() {
return author;
}
public int getPages() {
return pages;
}
public String getPublisher() {
return publisher;
}
public boolean isHardcover() {
return hardcover;
}
/**
* Calculates estimated reading time based on average reading speed.
*
* @param wordsPerMinute reading speed (typical: 200-250)
* @return estimated hours to read
*/
public double estimateReadingTime(int wordsPerMinute) {
if (pages == 0 || wordsPerMinute <= 0) {
return 0;
}
int avgWordsPerPage = 250;
int totalWords = pages * avgWordsPerPage;
return totalWords / (double)(wordsPerMinute * 60);
}
@Override
public String toString() {
return String.format("PrintedBook[ISBN=%s, Title='%s', Author='%s', Price=$%.2f, Year=%d, Pages=%d, %s]",
isbn, title, author, price, year, pages,
hardcover ? "Hardcover" : "Paperback");
}
}