VideoMaterial.java
package com.university.bookstore.model;
import java.util.Arrays;
import java.util.List;
/**
* Represents video content (movies, documentaries, educational videos).
* Implements both Material and Media interfaces to demonstrate multiple inheritance.
*
* @author Navid Mohaghegh
* @version 2.0
* @since 2024-09-15
*/
public class VideoMaterial extends Material implements Media {
private final String director;
private final int duration;
private final String format;
private final double fileSize;
private final MediaQuality quality;
private final VideoType videoType;
private final String rating;
private final List<String> cast;
private final boolean subtitlesAvailable;
private final String aspectRatio;
/**
* Types of video content.
*/
public enum VideoType {
MOVIE("Movie"),
DOCUMENTARY("Documentary"),
EDUCATIONAL("Educational"),
TV_SERIES("TV Series"),
SHORT_FILM("Short Film"),
TUTORIAL("Tutorial");
private final String label;
VideoType(String label) {
this.label = label;
}
@Override
public String toString() {
return label;
}
}
/**
* Creates a new VideoMaterial.
*
* @param id unique identifier (UPC/EAN code)
* @param title video title
* @param director director or producer
* @param price price in dollars
* @param year release year
* @param duration duration in minutes
* @param format video format (MP4, AVI, MKV, etc.)
* @param fileSize size in megabytes
* @param quality video quality
* @param videoType type of video
* @param rating content rating (G, PG, PG-13, R, etc.)
* @param cast list of main cast members
* @param subtitlesAvailable whether subtitles are included
* @param aspectRatio aspect ratio (16:9, 4:3, etc.)
*/
public VideoMaterial(String id, String title, String director, double price,
int year, int duration, String format, double fileSize,
MediaQuality quality, VideoType videoType, String rating,
List<String> cast, boolean subtitlesAvailable, String aspectRatio) {
super(id, title, price, year,
videoType == VideoType.DOCUMENTARY ? MaterialType.DOCUMENTARY : MaterialType.VIDEO);
this.director = validateStringField(director, "Director");
this.duration = validateDuration(duration);
this.format = validateStringField(format, "Format");
this.fileSize = validateFileSize(fileSize);
this.quality = quality != null ? quality : MediaQuality.HD;
this.videoType = videoType != null ? videoType : VideoType.MOVIE;
this.rating = rating != null ? rating : "NR";
this.cast = cast != null ? cast : Arrays.asList();
this.subtitlesAvailable = subtitlesAvailable;
this.aspectRatio = aspectRatio != null ? aspectRatio : "16:9";
}
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 director;
}
@Override
public String getDisplayInfo() {
return String.format("%s (%d) - Directed by %s, %s, %d min, Rated %s, $%.2f",
title, year, director, videoType, duration, rating, price);
}
@Override
public int getDuration() {
return duration;
}
@Override
public String getFormat() {
return format;
}
@Override
public double getFileSize() {
return fileSize;
}
@Override
public boolean isStreamingOnly() {
return fileSize > 4096;
}
@Override
public MediaQuality getQuality() {
return quality;
}
@Override
public double getDiscountRate() {
int currentYear = java.time.Year.now().getValue();
if (year < currentYear - 5) {
return 0.30;
} else if (year < currentYear - 2) {
return 0.15;
}
return 0.0;
}
/**
* Checks if this is a feature-length video.
*
* @return true if duration >= 60 minutes
*/
public boolean isFeatureLength() {
return duration >= 60;
}
/**
* Gets bandwidth requirement for streaming in Mbps.
*
* @return required bandwidth
*/
public double getStreamingBandwidth() {
switch (quality) {
case LOW: return 1.5;
case STANDARD: return 3.0;
case HIGH: return 5.0;
case HD: return 8.0;
case ULTRA_HD: return 25.0;
default: return 5.0;
}
}
/**
* Gets storage space required with compression.
*
* @param compressionRatio compression ratio (0.5 = 50% compression)
* @return compressed size in MB
*/
public double getCompressedSize(double compressionRatio) {
if (compressionRatio <= 0 || compressionRatio > 1) {
throw new IllegalArgumentException("Compression ratio must be between 0 and 1");
}
return fileSize * compressionRatio;
}
public String getDirector() {
return director;
}
public VideoType getVideoType() {
return videoType;
}
public String getRating() {
return rating;
}
public List<String> getCast() {
return cast;
}
public boolean hasSubtitles() {
return subtitlesAvailable;
}
public String getAspectRatio() {
return aspectRatio;
}
@Override
public String toString() {
return String.format("VideoMaterial[ID=%s, Title='%s', Director='%s', Type=%s, Duration=%dmin, Quality=%s, Rating=%s, Price=$%.2f]",
id, title, director, videoType, duration, quality, rating, price);
}
}