# 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());
        }
    }