ModernMaterialStore.java
package com.university.bookstore.api;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import com.university.bookstore.model.Material;
import com.university.bookstore.model.Media;
/**
* Modern interface for polymorphic material store with async operations.
* Extends the base MaterialStore with modern Java features.
*
* @author Navid Mohaghegh
* @version 4.0
* @since 2024-09-15
*/
public interface ModernMaterialStore extends MaterialStore {
/**
* Modern InventoryStats using Java record for immutability.
* Records provide built-in equals(), hashCode(), and toString() methods.
*
* @param totalCount total number of materials
* @param averagePrice average price of all materials
* @param medianPrice median price of all materials
* @param uniqueTypes number of unique material types
* @param mediaCount number of media materials
* @param printCount number of printed materials
*/
record ModernInventoryStats(
int totalCount,
double averagePrice,
double medianPrice,
int uniqueTypes,
int mediaCount,
int printCount
) {
/**
* Compact constructor for validation.
*/
public ModernInventoryStats {
if (totalCount < 0) {
throw new IllegalArgumentException("Total count cannot be negative");
}
if (averagePrice < 0) {
throw new IllegalArgumentException("Average price cannot be negative");
}
if (medianPrice < 0) {
throw new IllegalArgumentException("Median price cannot be negative");
}
if (uniqueTypes < 0) {
throw new IllegalArgumentException("Unique types cannot be negative");
}
if (mediaCount < 0) {
throw new IllegalArgumentException("Media count cannot be negative");
}
if (printCount < 0) {
throw new IllegalArgumentException("Print count cannot be negative");
}
}
/**
* Creates an empty stats instance.
*
* @return stats with all zero values
*/
public static ModernInventoryStats empty() {
return new ModernInventoryStats(0, 0, 0, 0, 0, 0);
}
/**
* Checks if the inventory is empty.
*
* @return true if total count is zero
*/
public boolean isEmpty() {
return totalCount == 0;
}
/**
* Gets the percentage of media materials.
*
* @return media percentage (0-100)
*/
public double getMediaPercentage() {
return totalCount > 0 ? (mediaCount * 100.0) / totalCount : 0;
}
/**
* Gets the percentage of print materials.
*
* @return print percentage (0-100)
*/
public double getPrintPercentage() {
return totalCount > 0 ? (printCount * 100.0) / totalCount : 0;
}
/**
* Creates a summary string for reporting.
*
* @return formatted summary
*/
public String getSummary() {
return String.format(
"""
Inventory Statistics:
- Total Items: %d
- Average Price: $%.2f
- Median Price: $%.2f
- Unique Types: %d
- Media Materials: %d (%.1f%%)
- Print Materials: %d (%.1f%%)
""",
totalCount, averagePrice, medianPrice, uniqueTypes,
mediaCount, getMediaPercentage(),
printCount, getPrintPercentage()
);
}
}
/**
* Batch operation result record.
*
* @param successful number of successful operations
* @param failed number of failed operations
* @param errors list of error messages
*/
record BatchOperationResult(
int successful,
int failed,
List<String> errors
) {
/**
* Checks if all operations were successful.
*
* @return true if no failures
*/
public boolean isCompleteSuccess() {
return failed == 0 && (errors == null || errors.isEmpty());
}
/**
* Gets the total number of operations.
*
* @return sum of successful and failed
*/
public int totalOperations() {
return successful + failed;
}
/**
* Gets the success rate.
*
* @return success percentage (0-100)
*/
public double successRate() {
int total = totalOperations();
return total > 0 ? (successful * 100.0) / total : 0;
}
}
/**
* Search criteria record for complex searches.
*
* @param title optional title search term
* @param creator optional creator search term
* @param type optional material type filter
* @param minPrice optional minimum price
* @param maxPrice optional maximum price
* @param yearFrom optional start year
* @param yearTo optional end year
*/
record SearchCriteria(
Optional<String> title,
Optional<String> creator,
Optional<Material.MaterialType> type,
Optional<Double> minPrice,
Optional<Double> maxPrice,
Optional<Integer> yearFrom,
Optional<Integer> yearTo
) {
/**
* Builder for SearchCriteria.
*/
public static class Builder {
private Optional<String> title = Optional.empty();
private Optional<String> creator = Optional.empty();
private Optional<Material.MaterialType> type = Optional.empty();
private Optional<Double> minPrice = Optional.empty();
private Optional<Double> maxPrice = Optional.empty();
private Optional<Integer> yearFrom = Optional.empty();
private Optional<Integer> yearTo = Optional.empty();
public Builder withTitle(String title) {
this.title = Optional.ofNullable(title);
return this;
}
public Builder withCreator(String creator) {
this.creator = Optional.ofNullable(creator);
return this;
}
public Builder withType(Material.MaterialType type) {
this.type = Optional.ofNullable(type);
return this;
}
public Builder withPriceRange(Double min, Double max) {
this.minPrice = Optional.ofNullable(min);
this.maxPrice = Optional.ofNullable(max);
return this;
}
public Builder withYearRange(Integer from, Integer to) {
this.yearFrom = Optional.ofNullable(from);
this.yearTo = Optional.ofNullable(to);
return this;
}
public SearchCriteria build() {
return new SearchCriteria(
title, creator, type,
minPrice, maxPrice,
yearFrom, yearTo
);
}
}
/**
* Creates a new builder.
*
* @return new builder instance
*/
public static Builder builder() {
return new Builder();
}
/**
* Checks if any criteria is specified.
*
* @return true if at least one criterion is present
*/
public boolean hasAnyCriteria() {
return title.isPresent() || creator.isPresent() || type.isPresent() ||
minPrice.isPresent() || maxPrice.isPresent() ||
yearFrom.isPresent() || yearTo.isPresent();
}
}
// Async operation methods
/**
* Adds material asynchronously.
*
* @param material the material to add
* @return CompletableFuture with the result
*/
CompletableFuture<Boolean> addMaterialAsync(Material material);
/**
* Finds material by ID asynchronously.
*
* @param id the material ID
* @return CompletableFuture with the result
*/
CompletableFuture<Optional<Material>> findByIdAsync(String id);
/**
* Searches by title asynchronously.
*
* @param title the title to search for
* @return CompletableFuture with the results
*/
CompletableFuture<List<Material>> searchByTitleAsync(String title);
/**
* Gets inventory statistics asynchronously.
*
* @return CompletableFuture with the statistics
*/
CompletableFuture<ModernInventoryStats> getModernInventoryStatsAsync();
/**
* Performs advanced search with multiple criteria.
*
* @param criteria the search criteria
* @return CompletableFuture with matching materials
*/
CompletableFuture<List<Material>> advancedSearchAsync(SearchCriteria criteria);
/**
* Adds multiple materials in batch.
*
* @param materials collection of materials to add
* @return CompletableFuture with batch operation result
*/
CompletableFuture<BatchOperationResult> addMaterialsBatchAsync(List<Material> materials);
/**
* Removes multiple materials in batch.
*
* @param ids collection of IDs to remove
* @return CompletableFuture with batch operation result
*/
CompletableFuture<BatchOperationResult> removeMaterialsBatchAsync(List<String> ids);
}