Drawing.java

package com.example.graphics;

import com.example.graphics.command.AddShapeCommand;
import com.example.graphics.command.CommandManager;
import com.example.graphics.command.MoveShapeCommand;
import com.example.graphics.command.RemoveShapeCommand;
import com.example.graphics.model.Shape;
import com.example.graphics.model.Triangle;
import com.example.graphics.observer.ShapeObserver;
import com.example.graphics.observer.ShapeSubject;
import com.example.graphics.render.Renderer;
import com.example.graphics.visitor.ShapeVisitor;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * Main drawing class that manages shapes and rendering.
 * This class integrates the various design patterns.
 */
public class Drawing implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private final List<Shape> shapes;
    private final CommandManager commandManager;
    private final ShapeSubject shapeSubject;
    private transient Renderer renderer;
    
    /**
     * Constructor initializes the drawing
     * @param renderer the renderer to use
     */
    public Drawing(Renderer renderer) {
        this.shapes = new ArrayList<>();
        this.commandManager = new CommandManager();
        this.shapeSubject = new ShapeSubject();
        this.renderer = renderer;
    }
    
    /**
     * Add a shape to the drawing using the Command pattern
     * @param shape the shape to add
     */
    public void addShape(Shape shape) {
        AddShapeCommand command = new AddShapeCommand(shapes, shape);
        commandManager.executeCommand(command);
        shapeSubject.notifyShapeAdded(shape);
    }
    
    /**
     * Remove a shape from the drawing using the Command pattern
     * @param shape the shape to remove
     */
    public void removeShape(Shape shape) {
        RemoveShapeCommand command = new RemoveShapeCommand(shapes, shape);
        commandManager.executeCommand(command);
        shapeSubject.notifyShapeRemoved(shape);
    }
    
    /**
     * Move a shape to a new position using the Command pattern
     * @param shape the shape to move
     * @param x the new x coordinate
     * @param y the new y coordinate
     */
    public void moveShape(Shape shape, int x, int y) {
        MoveShapeCommand command = new MoveShapeCommand(shape, x, y);
        commandManager.executeCommand(command);
        shapeSubject.notifyShapeModified(shape);
    }
    
    /**
     * Undo the last command
     * @return true if a command was undone, false if there are no commands to undo
     */
    public boolean undo() {
        return commandManager.undo();
    }
    
    /**
     * Redo the last undone command
     * @return true if a command was redone, false if there are no commands to redo
     */
    public boolean redo() {
        return commandManager.redo();
    }
    
    /**
     * Add an observer to be notified of shape changes
     * @param observer the observer to add
     */
    public void addObserver(ShapeObserver observer) {
        shapeSubject.addObserver(observer);
    }
    
    /**
     * Remove an observer from the notification list
     * @param observer the observer to remove
     */
    public void removeObserver(ShapeObserver observer) {
        shapeSubject.removeObserver(observer);
    }
    
    /**
     * Set the renderer to use
     * @param renderer the renderer to use
     */
    public void setRenderer(Renderer renderer) {
        this.renderer = renderer;
    }
    
    /**
     * Get the current renderer
     * @return the current renderer
     */
    public Renderer getRenderer() {
        return renderer;
    }
    
    /**
     * Get the list of shapes
     * @return the list of shapes
     */
    public List<Shape> getShapes() {
        return new ArrayList<>(shapes); // Return a copy to prevent direct modification
    }
    
    /**
     * Render all shapes using the current renderer
     */
    public void render() {
        renderer.clear();
        
        for (Shape shape : shapes) {
            if (shape instanceof com.example.graphics.model.Circle) {
                renderer.renderCircle((com.example.graphics.model.Circle) shape);
            } else if (shape instanceof com.example.graphics.model.Rectangle) {
                renderer.renderRectangle((com.example.graphics.model.Rectangle) shape);
            } else if (shape instanceof com.example.graphics.model.Line) {
                renderer.renderLine((com.example.graphics.model.Line) shape);
            } else if (shape instanceof com.example.graphics.model.Triangle) {
                renderer.renderTriangle((com.example.graphics.model.Triangle) shape);
            }
        }
        
        renderer.display();
    }
    
    /**
     * Apply a visitor to all shapes
     * @param visitor the visitor to apply
     */
    public void acceptVisitor(ShapeVisitor visitor) {
        for (Shape shape : shapes) {
            shape.accept(visitor);
        }
    }
}