MaterialFactory.java
package com.university.bookstore.factory;
import java.util.Map;
import com.university.bookstore.model.AudioBook;
import com.university.bookstore.model.EBook;
import com.university.bookstore.model.Magazine;
import com.university.bookstore.model.Material;
import com.university.bookstore.model.Media;
import com.university.bookstore.model.PrintedBook;
import com.university.bookstore.model.VideoMaterial;
/**
* Factory class for creating Material instances.
* Demonstrates the Factory pattern by providing a centralized
* way to create different types of materials without exposing
* their constructors directly.
*
* <p>This factory supports creating all material types including
* the new EBook class, with proper validation and error handling.</p>
*
* @author Navid Mohaghegh
* @version 2.0
* @since 2024-09-15
*/
public class MaterialFactory {
/**
* Creates a Material instance based on the specified type and properties.
*
* @param type the material type
* @param properties map of properties required for the material
* @return the created Material instance
* @throws IllegalArgumentException if type is null or properties are invalid
* @throws NullPointerException if required properties are missing
*/
public static Material createMaterial(String type, Map<String, Object> properties) {
if (type == null) {
throw new NullPointerException("Material type cannot be null");
}
if (properties == null) {
throw new NullPointerException("Properties cannot be null");
}
String normalizedType = type.trim().toUpperCase();
switch (normalizedType) {
case "BOOK":
case "PRINTED_BOOK":
return createPrintedBook(properties);
case "MAGAZINE":
return createMagazine(properties);
case "AUDIO_BOOK":
case "AUDIOBOOK":
return createAudioBook(properties);
case "VIDEO":
case "VIDEO_MATERIAL":
return createVideoMaterial(properties);
case "EBOOK":
case "E_BOOK":
return createEBook(properties);
default:
throw new IllegalArgumentException("Unsupported material type: " + type);
}
}
/**
* Creates a PrintedBook instance.
*/
private static PrintedBook createPrintedBook(Map<String, Object> properties) {
String isbn = getRequiredString(properties, "isbn");
String title = getRequiredString(properties, "title");
String author = getRequiredString(properties, "author");
double price = getRequiredDouble(properties, "price");
int year = getRequiredInteger(properties, "year");
int pages = getRequiredInteger(properties, "pages");
String publisher = getOptionalString(properties, "publisher", "Unknown");
boolean hardcover = getOptionalBoolean(properties, "hardcover", false);
return new PrintedBook(isbn, title, author, price, year, pages, publisher, hardcover);
}
/**
* Creates a Magazine instance.
*/
private static Magazine createMagazine(Map<String, Object> properties) {
String issn = getRequiredString(properties, "issn");
String title = getRequiredString(properties, "title");
String publisher = getRequiredString(properties, "publisher");
double price = getRequiredDouble(properties, "price");
int year = getRequiredInteger(properties, "year");
int issue = getRequiredInteger(properties, "issue");
String frequency = getOptionalString(properties, "frequency", "Monthly");
String category = getOptionalString(properties, "category", "General");
return new Magazine(issn, title, publisher, price, year, issue, frequency, category);
}
/**
* Creates an AudioBook instance.
* Note: This is a simplified version for demonstration purposes.
*/
private static AudioBook createAudioBook(Map<String, Object> properties) {
String isbn = getRequiredString(properties, "isbn");
String title = getRequiredString(properties, "title");
String author = getRequiredString(properties, "author");
String narrator = getRequiredString(properties, "narrator");
double price = getRequiredDouble(properties, "price");
int year = getRequiredInteger(properties, "year");
int duration = getRequiredInteger(properties, "duration");
String format = getOptionalString(properties, "format", "MP3");
double fileSize = getOptionalDouble(properties, "fileSize", 100.0);
Media.MediaQuality quality = getRequiredMediaQuality(properties, "quality");
String language = getOptionalString(properties, "language", "English");
boolean unabridged = getOptionalBoolean(properties, "unabridged", true);
return new AudioBook(isbn, title, author, narrator, price, year, duration,
format, fileSize, quality, language, unabridged);
}
/**
* Creates a VideoMaterial instance.
* Note: This is a simplified version for demonstration purposes.
*/
private static VideoMaterial createVideoMaterial(Map<String, Object> properties) {
String id = getRequiredString(properties, "id");
String title = getRequiredString(properties, "title");
String director = getRequiredString(properties, "director");
double price = getRequiredDouble(properties, "price");
int year = getRequiredInteger(properties, "year");
int duration = getRequiredInteger(properties, "duration");
String format = getOptionalString(properties, "format", "MP4");
double fileSize = getOptionalDouble(properties, "fileSize", 1000.0);
Media.MediaQuality quality = getRequiredMediaQuality(properties, "quality");
VideoMaterial.VideoType videoType = getRequiredVideoType(properties, "videoType");
String rating = getOptionalString(properties, "rating", "PG");
java.util.List<String> cast = getOptionalStringList(properties, "cast");
boolean subtitlesAvailable = getOptionalBoolean(properties, "subtitlesAvailable", false);
String aspectRatio = getOptionalString(properties, "aspectRatio", "16:9");
return new VideoMaterial(id, title, director, price, year, duration, format,
fileSize, quality, videoType, rating, cast,
subtitlesAvailable, aspectRatio);
}
/**
* Creates an EBook instance.
*/
private static EBook createEBook(Map<String, Object> properties) {
String id = getRequiredString(properties, "id");
String title = getRequiredString(properties, "title");
String author = getRequiredString(properties, "author");
double price = getRequiredDouble(properties, "price");
int year = getRequiredInteger(properties, "year");
String fileFormat = getRequiredString(properties, "fileFormat");
double fileSize = getRequiredDouble(properties, "fileSize");
boolean drmEnabled = getRequiredBoolean(properties, "drmEnabled");
int wordCount = getRequiredInteger(properties, "wordCount");
Media.MediaQuality quality = getRequiredMediaQuality(properties, "quality");
return new EBook(id, title, author, price, year, fileFormat, fileSize,
drmEnabled, wordCount, quality);
}
// Helper methods for extracting and validating properties
private static String getRequiredString(Map<String, Object> properties, String key) {
Object value = properties.get(key);
if (value == null) {
throw new NullPointerException("Required property '" + key + "' is missing");
}
if (!(value instanceof String)) {
throw new IllegalArgumentException("Property '" + key + "' must be a String, got: " + value.getClass().getSimpleName());
}
String str = (String) value;
if (str.trim().isEmpty()) {
throw new IllegalArgumentException("Property '" + key + "' cannot be empty");
}
return str.trim();
}
private static double getRequiredDouble(Map<String, Object> properties, String key) {
Object value = properties.get(key);
if (value == null) {
throw new NullPointerException("Required property '" + key + "' is missing");
}
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
if (value instanceof String) {
try {
return Double.parseDouble((String) value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Property '" + key + "' must be a valid number, got: " + value);
}
}
throw new IllegalArgumentException("Property '" + key + "' must be a number, got: " + value.getClass().getSimpleName());
}
private static int getRequiredInteger(Map<String, Object> properties, String key) {
Object value = properties.get(key);
if (value == null) {
throw new NullPointerException("Required property '" + key + "' is missing");
}
if (value instanceof Number) {
return ((Number) value).intValue();
}
if (value instanceof String) {
try {
return Integer.parseInt((String) value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Property '" + key + "' must be a valid integer, got: " + value);
}
}
throw new IllegalArgumentException("Property '" + key + "' must be an integer, got: " + value.getClass().getSimpleName());
}
private static boolean getRequiredBoolean(Map<String, Object> properties, String key) {
Object value = properties.get(key);
if (value == null) {
throw new NullPointerException("Required property '" + key + "' is missing");
}
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof String) {
String str = ((String) value).trim().toLowerCase();
if ("true".equals(str) || "1".equals(str) || "yes".equals(str)) {
return true;
}
if ("false".equals(str) || "0".equals(str) || "no".equals(str)) {
return false;
}
throw new IllegalArgumentException("Property '" + key + "' must be a valid boolean, got: " + value);
}
throw new IllegalArgumentException("Property '" + key + "' must be a boolean, got: " + value.getClass().getSimpleName());
}
private static Media.MediaQuality getRequiredMediaQuality(Map<String, Object> properties, String key) {
Object value = properties.get(key);
if (value == null) {
throw new NullPointerException("Required property '" + key + "' is missing");
}
if (value instanceof Media.MediaQuality) {
return (Media.MediaQuality) value;
}
if (value instanceof String) {
try {
return Media.MediaQuality.valueOf(((String) value).trim().toUpperCase());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Property '" + key + "' must be a valid MediaQuality, got: " + value);
}
}
throw new IllegalArgumentException("Property '" + key + "' must be a MediaQuality, got: " + value.getClass().getSimpleName());
}
private static String getOptionalString(Map<String, Object> properties, String key, String defaultValue) {
Object value = properties.get(key);
if (value == null) {
return defaultValue;
}
if (!(value instanceof String)) {
throw new IllegalArgumentException("Property '" + key + "' must be a String, got: " + value.getClass().getSimpleName());
}
String str = (String) value;
return str.trim().isEmpty() ? defaultValue : str.trim();
}
private static boolean getOptionalBoolean(Map<String, Object> properties, String key, boolean defaultValue) {
Object value = properties.get(key);
if (value == null) {
return defaultValue;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof String) {
String str = ((String) value).trim().toLowerCase();
if ("true".equals(str) || "1".equals(str) || "yes".equals(str)) {
return true;
}
if ("false".equals(str) || "0".equals(str) || "no".equals(str)) {
return false;
}
throw new IllegalArgumentException("Property '" + key + "' must be a valid boolean, got: " + value);
}
throw new IllegalArgumentException("Property '" + key + "' must be a boolean, got: " + value.getClass().getSimpleName());
}
private static double getOptionalDouble(Map<String, Object> properties, String key, double defaultValue) {
Object value = properties.get(key);
if (value == null) {
return defaultValue;
}
if (value instanceof Number) {
return ((Number) value).doubleValue();
}
if (value instanceof String) {
try {
return Double.parseDouble((String) value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Property '" + key + "' must be a valid number, got: " + value);
}
}
throw new IllegalArgumentException("Property '" + key + "' must be a number, got: " + value.getClass().getSimpleName());
}
private static VideoMaterial.VideoType getRequiredVideoType(Map<String, Object> properties, String key) {
Object value = properties.get(key);
if (value == null) {
throw new NullPointerException("Required property '" + key + "' is missing");
}
if (value instanceof VideoMaterial.VideoType) {
return (VideoMaterial.VideoType) value;
}
if (value instanceof String) {
try {
return VideoMaterial.VideoType.valueOf(((String) value).trim().toUpperCase());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Property '" + key + "' must be a valid VideoType, got: " + value);
}
}
throw new IllegalArgumentException("Property '" + key + "' must be a VideoType, got: " + value.getClass().getSimpleName());
}
@SuppressWarnings("unchecked")
private static java.util.List<String> getOptionalStringList(Map<String, Object> properties, String key) {
Object value = properties.get(key);
if (value == null) {
return java.util.Arrays.asList();
}
if (value instanceof java.util.List) {
return (java.util.List<String>) value;
}
if (value instanceof String) {
String str = (String) value;
if (str.trim().isEmpty()) {
return java.util.Arrays.asList();
}
return java.util.Arrays.asList(str.split(","));
}
throw new IllegalArgumentException("Property '" + key + "' must be a List<String> or String, got: " + value.getClass().getSimpleName());
}
}