AudioBook.java
package com.university.bookstore.model;
/**
* Represents an audio book that implements both Material and Media interfaces.
* Demonstrates multiple inheritance through interfaces.
*
* @author Navid Mohaghegh
* @version 2.0
* @since 2024-09-15
*/
public class AudioBook extends Material implements Media {
private final String isbn;
private final String author;
private final String narrator;
private final int duration;
private final String format;
private final double fileSize;
private final boolean streamingOnly;
private final MediaQuality quality;
private final String language;
private final boolean unabridged;
/**
* Creates a new AudioBook with full specifications.
*
* @param isbn ISBN identifier
* @param title book title
* @param author original author
* @param narrator voice narrator
* @param price price in dollars
* @param year publication year
* @param duration duration in minutes
* @param format audio format (MP3, M4B, etc.)
* @param fileSize size in megabytes
* @param quality audio quality level
* @param language narration language
* @param unabridged true if unabridged version
*/
public AudioBook(String isbn, String title, String author, String narrator,
double price, int year, int duration, String format,
double fileSize, MediaQuality quality, String language,
boolean unabridged) {
super(validateIsbn(isbn), title, price, year, MaterialType.AUDIO_BOOK);
this.isbn = this.id;
this.author = validateStringField(author, "Author");
this.narrator = validateStringField(narrator, "Narrator");
this.duration = validateDuration(duration);
this.format = validateStringField(format, "Format");
this.fileSize = validateFileSize(fileSize);
this.streamingOnly = false;
this.quality = quality != null ? quality : MediaQuality.STANDARD;
this.language = validateStringField(language, "Language");
this.unabridged = unabridged;
}
private static String validateIsbn(String isbn) {
if (isbn == null) {
throw new NullPointerException("ISBN cannot be null");
}
String cleaned = isbn.replaceAll("-", "").trim();
if (cleaned.length() < 10 || cleaned.length() > 13) {
throw new IllegalArgumentException("Invalid ISBN: " + isbn);
}
return cleaned;
}
private int validateDuration(int duration) {
if (duration <= 0) {
throw new IllegalArgumentException(
"Duration must be positive. Provided: " + duration);
}
return duration;
}
private double validateFileSize(double fileSize) {
if (fileSize <= 0) {
throw new IllegalArgumentException(
"File size must be positive. Provided: " + fileSize);
}
return fileSize;
}
@Override
public String getCreator() {
return author + " (Narrated by " + narrator + ")";
}
@Override
public String getDisplayInfo() {
return String.format("%s by %s, narrated by %s - %s, %d hours %d minutes, $%.2f",
title, author, narrator,
unabridged ? "Unabridged" : "Abridged",
duration / 60, duration % 60, price);
}
@Override
public int getDuration() {
return duration;
}
@Override
public String getFormat() {
return format;
}
@Override
public double getFileSize() {
return fileSize;
}
@Override
public boolean isStreamingOnly() {
return streamingOnly;
}
@Override
public MediaQuality getQuality() {
return quality;
}
@Override
public double getDiscountRate() {
if (!unabridged) {
return 0.10;
}
return 0.0;
}
/**
* Calculates listening sessions based on daily listening time.
*
* @param minutesPerDay daily listening time
* @return number of days to complete
*/
public int calculateListeningSessions(int minutesPerDay) {
if (minutesPerDay <= 0) {
return 0;
}
return (int) Math.ceil((double) duration / minutesPerDay);
}
/**
* Gets playback speed adjusted duration.
*
* @param speed playback speed (1.0 = normal, 1.5 = 1.5x, etc.)
* @return adjusted duration in minutes
*/
public int getAdjustedDuration(double speed) {
if (speed <= 0) {
throw new IllegalArgumentException("Speed must be positive");
}
return (int) Math.ceil(duration / speed);
}
/** Gets the ISBN. @return the ISBN */
public String getIsbn() {
return isbn;
}
/** Gets the author. @return the author */
public String getAuthor() {
return author;
}
/** Gets the narrator. @return the narrator */
public String getNarrator() {
return narrator;
}
/** Gets the language. @return the language */
public String getLanguage() {
return language;
}
/** Checks if unabridged. @return true if unabridged */
public boolean isUnabridged() {
return unabridged;
}
@Override
public String toString() {
return String.format("AudioBook[ISBN=%s, Title='%s', Author='%s', Narrator='%s', Duration=%dmin, Format=%s, %s, Price=$%.2f]",
isbn, title, author, narrator, duration, format,
unabridged ? "Unabridged" : "Abridged", price);
}
}