MaterialDecorator.java

package com.university.bookstore.decorator;

import java.util.Objects;

import com.university.bookstore.model.Material;

/**
 * Abstract base class for material decorators in the Decorator pattern.
 * Provides a common interface for decorating materials with additional features.
 * 
 * <p>This class implements the Material interface and delegates all operations
 * to the wrapped material, allowing subclasses to override specific methods
 * to add new functionality without modifying the original material classes.</p>
 * 
 * @author Navid Mohaghegh
 * @version 3.0
 * @since 2024-09-15
 */
public abstract class MaterialDecorator extends Material {
    
    protected final Material decoratedMaterial;
    
    /**
     * Creates a new material decorator wrapping the specified material.
     * 
     * @param material the material to decorate
     * @throws IllegalArgumentException if material is null
     */
    public MaterialDecorator(Material material) {
        super(material.getId(), material.getTitle(), material.getPrice(), material.getYear(), material.getType());
        this.decoratedMaterial = Objects.requireNonNull(material, "Material cannot be null");
    }
    
    // Implement abstract methods from Material by delegating to wrapped material
    @Override
    public String getCreator() {
        return decoratedMaterial.getCreator();
    }
    
    @Override
    public String getDisplayInfo() {
        return decoratedMaterial.getDisplayInfo();
    }
    
    @Override
    public double getDiscountRate() {
        return decoratedMaterial.getDiscountRate();
    }
    
    /**
     * Gets the decorated material.
     * 
     * @return the wrapped material
     */
    public Material getDecoratedMaterial() {
        return decoratedMaterial;
    }
    
    /**
     * Gets the base material (unwraps all decorators).
     * 
     * @return the original material without any decorations
     */
    public Material getBaseMaterial() {
        Material current = decoratedMaterial;
        while (current instanceof MaterialDecorator) {
            current = ((MaterialDecorator) current).getDecoratedMaterial();
        }
        return current;
    }
    
    /**
     * Gets the number of decorators applied to this material.
     * 
     * @return the decorator count
     */
    public int getDecoratorCount() {
        int count = 0;
        Material current = decoratedMaterial;
        while (current instanceof MaterialDecorator) {
            count++;
            current = ((MaterialDecorator) current).getDecoratedMaterial();
        }
        return count;
    }
    
    /**
     * Checks if this material has any decorators applied.
     * 
     * @return true if decorators are applied
     */
    public boolean hasDecorators() {
        return getDecoratorCount() > 0;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        
        MaterialDecorator that = (MaterialDecorator) obj;
        return Objects.equals(decoratedMaterial, that.decoratedMaterial);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(decoratedMaterial);
    }
    
    @Override
    public String toString() {
        return String.format("%s[%s]", getClass().getSimpleName(), decoratedMaterial.getTitle());
    }
}