PersistenceManager.java

/**
 * PersistenceManager 是图形对象的持久化管理器,使用单例模式确保全局唯一实例。
 * 负责图形的序列化和反序列化,使用GSON库实现JSON格式的持久化。
 *
 * <p>主要功能:
 * <ul>
 *   <li>将图形列表保存到JSON文件</li>
 *   <li>从JSON文件加载图形列表</li>
 *   <li>处理多态类型的序列化/反序列化</li>
 * </ul>
 * </p>
 *
 * <p>使用示例:
 * <pre>{@code
 * List<Shape> shapes = ...;
 * PersistenceManager manager = PersistenceManager.getInstance();
 * manager.saveShapesToFile(shapes, "shapes.json");
 * List<Shape> loaded = manager.loadShapesFromFile("shapes.json");
 * }</pre>
 * </p>
 *
 * <p>线程安全说明:Gson实例是线程安全的,可以并发调用序列化/反序列化方法。</p>
 *
 * @see RuntimeTypeAdapterFactory 用于处理多态类型序列化
 * @see Gson Google的JSON处理库
 * @author liying
 * @since 1.0
 * @version 1.0
 */
package com.example.renderer.singleton;

import com.example.renderer.factory.*;
import com.example.renderer.util.RuntimeTypeAdapterFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import com.google.gson.JsonIOException;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;

/**
 * 图形持久化管理器,使用单例模式
 * 
 * <p>负责图形的序列化和反序列化,主要功能:
 * <ul>
 *   <li>将图形列表保存为JSON文件</li>
 *   <li>从JSON文件加载图形列表</li>
 *   <li>处理多态类型的序列化</li>
 * </ul>
 * 
 * <p>使用Gson库实现JSON序列化
 */
public class PersistenceManager {
    private static final PersistenceManager INSTANCE = new PersistenceManager();
    private final Gson gson;

    private PersistenceManager() {
        // 创建支持多态类型的Gson适配器工厂
        RuntimeTypeAdapterFactory<com.example.renderer.factory.Shape> adapterFactory =
                RuntimeTypeAdapterFactory.of(Shape.class, "type")
                        .registerSubtype(Circle.class, "Circle")
                        .registerSubtype(Rectangle.class, "Rectangle")
                        .registerSubtype(Ellipse.class, "Ellipse")
                        .registerSubtype(Triangle.class, "Triangle");

        // 配置Gson实例
        gson = new GsonBuilder()
                .registerTypeAdapterFactory(adapterFactory) // 注册类型适配器
                .setPrettyPrinting() // 美化输出
                .create();
    }

    /**
     * 获取PersistenceManager单例实例
     * 
     * <p>该方法线程安全,始终返回同一个实例。</p>
     * 
     * @return 全局唯一的PersistenceManager实例(永不为null)
     * @see #PersistenceManager() 私有构造函数
     */
    /**
     * 获取线程安全的PersistenceManager单例实例
     * 
     * <p>该方法使用类初始化保证线程安全,无需同步</p>
     * 
     * @return 全局唯一的PersistenceManager实例
     */
    public static PersistenceManager getInstance() {
        return INSTANCE;
    }

    /**
     * 将图形列表序列化为JSON格式并保存到指定文件
     * 
     * <p>该方法会覆盖目标文件的所有内容,使用UTF-8编码写入。</p>
     * 
     * @param shapes 要保存的图形列表(非null)
     * @param filePath 目标文件路径(非null或空)
     * @throws NullPointerException 如果shapes或filePath为null
     * @throws IllegalArgumentException 如果filePath为空字符串
     * @throws IOException 如果文件写入失败或路径不可访问
     * @throws JsonIOException 如果JSON序列化过程中发生错误
     */
    public void saveShapesToFile(List<com.example.renderer.factory.Shape> shapes, String filePath) throws IOException {
        Objects.requireNonNull(shapes, "Shapes list cannot be null");
        if (filePath == null || filePath.trim().isEmpty()) {
            throw new IllegalArgumentException("File path cannot be null or empty");
        }
        try (FileWriter writer = new FileWriter(filePath)) {
            gson.toJson(shapes, writer);
        }
    }

    /**
     * 从JSON文件加载图形列表
     * 
     * <p>该方法会读取指定JSON文件并反序列化为图形对象列表。
     * 支持处理多态类型的图形对象。</p>
     * 
     * @param filePath 要读取的文件路径(非null或空)
     * @return 包含所有反序列化图形对象的不可修改列表
     * @throws NullPointerException 如果filePath为null
     * @throws IllegalArgumentException 如果filePath为空字符串
     * @throws FileNotFoundException 如果指定文件不存在
     * @throws IOException 如果文件读取失败
     * @throws JsonParseException 如果JSON格式不合法
     * @throws JsonSyntaxException 如果JSON与目标类型不匹配
     * @throws IllegalStateException 如果反序列化后的数组为null
     */
    public List<com.example.renderer.factory.Shape> loadShapesFromFile(String filePath) throws IOException {
        Objects.requireNonNull(filePath, "File path cannot be null");
        if (filePath.trim().isEmpty()) {
            throw new IllegalArgumentException("File path cannot be empty");
        }
        try (FileReader reader = new FileReader(filePath)) {
            // 使用数组形式反序列化为 List
            Shape[] shapesArray = gson.fromJson(reader, Shape[].class);
            return List.of(shapesArray);
        }
    }
}