From 6ef835fd312d61cb44ce2d488c47ff4d8a36118e Mon Sep 17 00:00:00 2001 From: Dominik Krenn Date: Mon, 15 Sep 2025 15:35:19 +0200 Subject: [PATCH] Implement LocalStorage class for data serialization and deserialization with database integration --- src/squirrel/LocalStorage.java | 95 ++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/squirrel/LocalStorage.java diff --git a/src/squirrel/LocalStorage.java b/src/squirrel/LocalStorage.java new file mode 100644 index 0000000..c38f81b --- /dev/null +++ b/src/squirrel/LocalStorage.java @@ -0,0 +1,95 @@ +package squirrel; + +import java.sql.*; +import java.util.*; + +public class LocalStorage { + private final Connection conn; + + public LocalStorage() { + this.conn = Database.getConnection(); + } + + // Save: serialize constructor arguments + public void save(String key, T value) { + try { + var ctor = value.getClass().getDeclaredConstructors()[0]; + ctor.setAccessible(true); + + var fields = value.getClass().getDeclaredFields(); + List pairs = new ArrayList<>(); + + for (var f : fields) { + f.setAccessible(true); + Object v = f.get(value); + pairs.add(f.getName() + "=" + (v == null ? "null" : v.toString())); + } + + try (PreparedStatement ps = conn.prepareStatement( + "INSERT INTO storage(key, data) VALUES(?, ?) " + + "ON CONFLICT(key) DO UPDATE SET data=excluded.data" + )) { + ps.setString(1, key); + ps.setString(2, String.join(";", pairs)); + ps.executeUpdate(); + } + } catch (Exception e) { + throw new RuntimeException("Save failed", e); + } + } + + // Load: reconstruct using the constructor of clazz + @SuppressWarnings("unchecked") + public T load(String key, Class clazz) { + try (PreparedStatement ps = conn.prepareStatement( + "SELECT data FROM storage WHERE key=?" + )) { + ps.setString(1, key); + ResultSet rs = ps.executeQuery(); + if (!rs.next()) return null; + + String data = rs.getString("data"); + Map map = new LinkedHashMap<>(); + for (String pair : data.split(";")) { + if (pair.isBlank()) continue; + int idx = pair.indexOf('='); + if (idx == -1) continue; + map.put(pair.substring(0, idx), pair.substring(idx + 1)); + } + + var ctor = clazz.getDeclaredConstructors()[0]; + ctor.setAccessible(true); + Class[] paramTypes = ctor.getParameterTypes(); + Object[] converted = new Object[paramTypes.length]; + + int i = 0; + for (Class pt : paramTypes) { + String raw = map.values().toArray(new String[0])[i]; + if (pt == int.class || pt == Integer.class) { + converted[i] = Integer.parseInt(raw); + } else if (pt == long.class || pt == Long.class) { + converted[i] = Long.parseLong(raw); + } else if (pt == double.class || pt == Double.class) { + converted[i] = Double.parseDouble(raw); + } else if (pt == String.class) { + converted[i] = raw; + } else { + throw new IllegalArgumentException("Unsupported param type: " + pt); + } + i++; + } + + return (T) ctor.newInstance(converted); + } catch (Exception e) { + throw new RuntimeException("Load failed", e); + } + } + + public void flush() { + try (Statement stmt = conn.createStatement()) { + stmt.executeUpdate("DELETE FROM storage"); + } catch (Exception e) { + throw new RuntimeException("Flush failed", e); + } + } +}