DigitalAnnotationDecorator.java

package com.university.bookstore.decorator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import com.university.bookstore.model.Material;

/**
 * Decorator that adds digital annotation functionality to materials.
 * Increases the price and provides annotation management capabilities.
 * 
 * @author Navid Mohaghegh
 * @version 3.0
 * @since 2024-09-15
 */
public class DigitalAnnotationDecorator extends MaterialDecorator {
    
    private static final double ANNOTATION_COST = 2.99;
    private final List<String> annotations;
    
    /**
     * Creates a new digital annotation decorator.
     * 
     * @param material the material to add annotations to
     */
    public DigitalAnnotationDecorator(Material material) {
        super(material);
        this.annotations = new ArrayList<>();
    }
    
    @Override
    public double getPrice() {
        return decoratedMaterial.getPrice() + ANNOTATION_COST;
    }
    
    @Override
    public String getDisplayInfo() {
        return decoratedMaterial.getDisplayInfo() + 
               String.format(" [Digital Annotations: %d notes (+$%.2f)]", 
                           annotations.size(), ANNOTATION_COST);
    }
    
    /**
     * Adds an annotation to the material.
     * 
     * @param annotation the annotation text
     * @throws IllegalArgumentException if annotation is null or empty
     */
    public void addAnnotation(String annotation) {
        if (annotation == null || annotation.trim().isEmpty()) {
            throw new IllegalArgumentException("Annotation cannot be null or empty");
        }
        annotations.add(annotation.trim());
    }
    
    /**
     * Removes an annotation by index.
     * 
     * @param index the index of the annotation to remove
     * @return the removed annotation
     * @throws IndexOutOfBoundsException if index is invalid
     */
    public String removeAnnotation(int index) {
        if (index < 0 || index >= annotations.size()) {
            throw new IndexOutOfBoundsException("Invalid annotation index: " + index);
        }
        return annotations.remove(index);
    }
    
    /**
     * Gets all annotations.
     * 
     * @return unmodifiable list of annotations
     */
    public List<String> getAnnotations() {
        return Collections.unmodifiableList(annotations);
    }
    
    /**
     * Gets the number of annotations.
     * 
     * @return the annotation count
     */
    public int getAnnotationCount() {
        return annotations.size();
    }
    
    /**
     * Clears all annotations.
     */
    public void clearAnnotations() {
        annotations.clear();
    }
    
    /**
     * Gets the digital annotation cost.
     * 
     * @return the annotation cost
     */
    public double getDigitalAnnotationCost() {
        return ANNOTATION_COST;
    }
    
    /**
     * Checks if the material has any annotations.
     * 
     * @return true if annotations exist
     */
    public boolean hasAnnotations() {
        return !annotations.isEmpty();
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        if (!super.equals(obj)) return false;
        
        DigitalAnnotationDecorator that = (DigitalAnnotationDecorator) obj;
        return Objects.equals(annotations, that.annotations);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), annotations);
    }
}