Compare commits
8 Commits
dc1b85a774
...
f9244082a2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9244082a2 | ||
|
|
6586e59959 | ||
|
|
6ef835fd31 | ||
|
|
bd61b1f2a3 | ||
|
|
1855d2000e | ||
|
|
6b774aec63 | ||
|
|
6d2b939bc6 | ||
|
|
fc1aca80d2 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ instance/
|
||||
*.class
|
||||
*.db
|
||||
.vscode/
|
||||
sources.txt
|
||||
|
||||
59
Main.java
59
Main.java
@ -1,37 +1,42 @@
|
||||
import quick.Interface;
|
||||
import squirrel.Database;
|
||||
import squirrel.ModelManager;
|
||||
import interfaces.Index;
|
||||
import interfaces.Login;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import src.models.UserModel;
|
||||
import src.models.squirrel.Database;
|
||||
import src.models.squirrel.ModelManager;
|
||||
import squirrel.LocalStorage;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) throws SQLException {
|
||||
public static void main(String[] args) {
|
||||
ModelManager.initializeModels();
|
||||
Database.migrate = conn -> src.Migration.run(conn);
|
||||
Database.getConnection();
|
||||
|
||||
UserModel userModel = ModelManager.get(UserModel.class);
|
||||
List<UserModel> users = userModel.where(java.util.Collections.emptyMap());
|
||||
for (UserModel user : users) {
|
||||
user.set("name", user.get("name") + " Updated");
|
||||
user.save();
|
||||
System.out.println(user);
|
||||
}
|
||||
|
||||
Connection conn = Database.getConnection();
|
||||
// Example: Run a simple SQL query
|
||||
try (var stmt = conn.createStatement();
|
||||
var rs = stmt.executeQuery("SELECT COUNT(*) AS user_count FROM users")) {
|
||||
if (rs.next()) {
|
||||
int userCount = rs.getInt("user_count");
|
||||
System.out.println("Total users: " + userCount);
|
||||
Migration.run(conn);
|
||||
|
||||
Interface app = new Interface(Login.class);
|
||||
app.registerView(Index.class);
|
||||
app.registerView(Login.class);
|
||||
|
||||
class Point {
|
||||
public int x;
|
||||
public int y;
|
||||
public int z = 0;
|
||||
public String name = "origin";
|
||||
|
||||
public Point(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Database.close();
|
||||
Point p = new Point(3, 4);
|
||||
System.out.println("Point: (" + p.x + ", " + p.y + ", " + p.z + ", " + p.name + ")");
|
||||
|
||||
LocalStorage storage = new LocalStorage();
|
||||
storage.save("point1", p);
|
||||
|
||||
Point loaded = storage.load("point1", Point.class);
|
||||
System.out.println("Loaded Point: (" + loaded.x + ", " + loaded.y + ", " + loaded.z + ", " + loaded.name + ")");
|
||||
|
||||
storage.flush();
|
||||
}
|
||||
}
|
||||
|
||||
9
run.sh
9
run.sh
@ -8,16 +8,21 @@ if ! command -v java >/dev/null 2>&1; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# build list of source files
|
||||
find src -name "*.java" > sources.txt
|
||||
echo "Main.java" >> sources.txt
|
||||
if ! javac -d build -cp "./extern/sqlite-jdbc-3.50.3.0.jar" @sources.txt Main.java; then
|
||||
|
||||
# compile with all extern jars
|
||||
if ! javac -d build -cp "extern/*" @sources.txt Main.java; then
|
||||
echo "Error: javac failed to compile sources."
|
||||
exit 1
|
||||
fi
|
||||
rm sources.txt
|
||||
|
||||
# create instance dir if not exists
|
||||
if [ ! -d "instance" ]; then
|
||||
mkdir instance
|
||||
fi
|
||||
|
||||
java -cp "build:./extern/sqlite-jdbc-3.50.3.0.jar" Main
|
||||
# run with all extern jars
|
||||
java -cp "build:extern/*" Main
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
package src;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class Migration {
|
||||
public static void run(Connection conn) {
|
||||
try (Statement stmt = conn.createStatement()) {
|
||||
System.out.println("Running migrations...");
|
||||
stmt.execute("""
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@ -52,6 +51,15 @@ public class Migration {
|
||||
)
|
||||
""");
|
||||
|
||||
stmt.execute("""
|
||||
CREATE TABLE IF NOT EXISTS storage (
|
||||
key TEXT PRIMARY KEY,
|
||||
type TEXT,
|
||||
data TEXT NOT NULL
|
||||
)
|
||||
""");
|
||||
System.out.println("Migrations completed.");
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
44
src/interfaces/Index.java
Normal file
44
src/interfaces/Index.java
Normal file
@ -0,0 +1,44 @@
|
||||
package interfaces;
|
||||
|
||||
import quick.Action;
|
||||
import quick.View;
|
||||
import quick.Color;
|
||||
|
||||
public class Index extends View {
|
||||
private boolean yes = false;
|
||||
|
||||
public Boolean init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void draw() {
|
||||
terminal.print(Color.ANSI_CYAN + "Index View" + Color.ANSI_RESET);
|
||||
terminal.print("\n");
|
||||
terminal.print("This is the index view. Use commands to navigate.\n");
|
||||
terminal.print("\n");
|
||||
terminal.print("Available commands:\n");
|
||||
terminal.print(" - test: go test view\n");
|
||||
terminal.print(" - confirm: confirm\n");
|
||||
|
||||
if (yes) {
|
||||
terminal.print(Color.ANSI_GREEN + "You have confirmed yes!" + Color.ANSI_RESET + "\n");
|
||||
} else {
|
||||
terminal.print(Color.ANSI_YELLOW + "You have not confirmed yes." + Color.ANSI_RESET + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
public Action onCommand(String command) {
|
||||
if (command.equals("confirm")) {
|
||||
yes = true;
|
||||
}
|
||||
if (command.equals("test")) {
|
||||
return actions.redirect(Login.class);
|
||||
}
|
||||
|
||||
return actions.nop();
|
||||
}
|
||||
|
||||
public Boolean onSelection(String userInputRaw) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
114
src/interfaces/Login.java
Normal file
114
src/interfaces/Login.java
Normal file
@ -0,0 +1,114 @@
|
||||
package interfaces;
|
||||
|
||||
import quick.Action;
|
||||
import quick.View;
|
||||
import quick.Color;
|
||||
|
||||
public class Login extends View {
|
||||
private String username = "";
|
||||
private String password = "";
|
||||
private boolean loginSuccess = false;
|
||||
private int loginTries = 0;
|
||||
private String editField = null;
|
||||
private String error = "";
|
||||
|
||||
@Override
|
||||
public Boolean init() {
|
||||
username = "";
|
||||
password = "";
|
||||
loginSuccess = false;
|
||||
loginTries = 0;
|
||||
editField = null;
|
||||
error = "";
|
||||
viewPrompt = "> ";
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
terminal.print(Color.ANSI_CYAN + "=== Login ===" + Color.ANSI_RESET + "\n");
|
||||
|
||||
terminal.print("Username: " + (username.isEmpty() ? "<not set>" : username) + "\n");
|
||||
terminal.print("Password: " + (password.isEmpty() ? "<not set>" : "*".repeat(password.length())) + "\n");
|
||||
|
||||
if (loginSuccess) {
|
||||
terminal.print(Color.ANSI_GREEN + "Login successful!" + Color.ANSI_RESET + "\n");
|
||||
terminal.print("Press any key to continue..." + "\n");
|
||||
viewPrompt = "> ";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!error.isEmpty()) {
|
||||
terminal.print(Color.ANSI_RED + error + Color.ANSI_RESET + "\n");
|
||||
}
|
||||
|
||||
terminal.print("\nCommands:\n");
|
||||
terminal.print(" edit username - enter username\n");
|
||||
terminal.print(" edit password - enter password\n");
|
||||
terminal.print(" login - attempt login\n");
|
||||
terminal.print(" back - go back\n");
|
||||
|
||||
if (editField != null) {
|
||||
viewPrompt = "Enter " + editField + ": ";
|
||||
} else {
|
||||
viewPrompt = "> ";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action onCommand(String command) {
|
||||
String cmd = command.trim().toLowerCase();
|
||||
|
||||
if (cmd.isEmpty()) {
|
||||
error = "Empty input. Type a command.";
|
||||
return actions.nop();
|
||||
}
|
||||
|
||||
if (cmd.equals("back")) {
|
||||
return actions.redirect(Index.class);
|
||||
}
|
||||
|
||||
if (cmd.equals("login")) {
|
||||
if (username.equals("a") && password.equals("a")) {
|
||||
loginSuccess = true;
|
||||
error = "";
|
||||
} else {
|
||||
loginTries++;
|
||||
error = "Login failed (" + loginTries + " attempts).";
|
||||
}
|
||||
return actions.nop();
|
||||
}
|
||||
|
||||
if (cmd.startsWith("edit")) {
|
||||
if (cmd.contains("username")) {
|
||||
editField = "username";
|
||||
error = "";
|
||||
} else if (cmd.contains("password")) {
|
||||
editField = "password";
|
||||
error = "";
|
||||
} else {
|
||||
error = "Unknown field to edit. Use: edit username | edit password";
|
||||
}
|
||||
return actions.nop();
|
||||
}
|
||||
|
||||
if (editField != null) {
|
||||
if (editField.equals("username")) {
|
||||
username = command.trim();
|
||||
} else if (editField.equals("password")) {
|
||||
password = command.trim();
|
||||
}
|
||||
editField = null;
|
||||
error = "";
|
||||
return actions.nop();
|
||||
}
|
||||
|
||||
error = "Unknown command: " + command;
|
||||
return actions.nop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean onSelection(String userInputRaw) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
package src.models;
|
||||
package models;
|
||||
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
|
||||
import src.models.squirrel.Model;
|
||||
import src.models.squirrel.ModelManager;
|
||||
import squirrel.Model;
|
||||
import squirrel.ModelManager;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
@ -25,7 +25,7 @@ public class UserModel extends Model {
|
||||
private static final int ITERATIONS = 65536;
|
||||
private static final int KEY_LENGTH = 256;
|
||||
|
||||
public static UserModel register(String name, String email, String password) {
|
||||
public UserModel register(String name, String email, String password) {
|
||||
if (password == null || password.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Password cannot be empty");
|
||||
}
|
||||
@ -46,7 +46,29 @@ public class UserModel extends Model {
|
||||
return user;
|
||||
}
|
||||
|
||||
public static String hashPassword(String password, byte[] salt) {
|
||||
public UserModel authenticate(String email, String password) {
|
||||
UserModel userModel = ModelManager.get(UserModel.class);
|
||||
|
||||
List<UserModel> users = userModel.where(Map.of("email", email));
|
||||
if (users == null || users.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UserModel user = users.get(0);
|
||||
String storedHash = (String) user.get("password_hash");
|
||||
if (storedHash == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] salt = Base64.getDecoder().decode(storedHash);
|
||||
if (!validatePassword(password, storedHash, salt)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public String hashPassword(String password, byte[] salt) {
|
||||
try {
|
||||
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
||||
@ -57,14 +79,14 @@ public class UserModel extends Model {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getSalt() {
|
||||
public byte[] getSalt() {
|
||||
SecureRandom sr = new SecureRandom();
|
||||
byte[] salt = new byte[16];
|
||||
sr.nextBytes(salt);
|
||||
return salt;
|
||||
}
|
||||
|
||||
public static boolean validatePassword(String password, String storedHash, byte[] salt) {
|
||||
public boolean validatePassword(String password, String storedHash, byte[] salt) {
|
||||
try {
|
||||
String newHash = hashPassword(password, salt);
|
||||
return Arrays.equals(Base64.getDecoder().decode(storedHash), Base64.getDecoder().decode(newHash));
|
||||
|
||||
6
src/quick/Action.java
Normal file
6
src/quick/Action.java
Normal file
@ -0,0 +1,6 @@
|
||||
package quick;
|
||||
|
||||
public abstract class Action {
|
||||
protected Global global = Global.getInstance();
|
||||
public abstract Boolean execute(Interface base);
|
||||
}
|
||||
22
src/quick/Color.java
Normal file
22
src/quick/Color.java
Normal file
@ -0,0 +1,22 @@
|
||||
package quick;
|
||||
|
||||
public class Color {
|
||||
public static final String ANSI_RESET = "\u001B[0m";
|
||||
public static final String ANSI_BLACK = "\u001B[30m";
|
||||
public static final String ANSI_RED = "\u001B[31m";
|
||||
public static final String ANSI_GREEN = "\u001B[32m";
|
||||
public static final String ANSI_YELLOW = "\u001B[33m";
|
||||
public static final String ANSI_BLUE = "\u001B[34m";
|
||||
public static final String ANSI_PURPLE = "\u001B[35m";
|
||||
public static final String ANSI_CYAN = "\u001B[36m";
|
||||
public static final String ANSI_WHITE = "\u001B[37m";
|
||||
|
||||
public static final String ANSI_BLACK_BACKGROUND = "\u001B[40m";
|
||||
public static final String ANSI_RED_BACKGROUND = "\u001B[41m";
|
||||
public static final String ANSI_GREEN_BACKGROUND = "\u001B[42m";
|
||||
public static final String ANSI_YELLOW_BACKGROUND = "\u001B[43m";
|
||||
public static final String ANSI_BLUE_BACKGROUND = "\u001B[44m";
|
||||
public static final String ANSI_PURPLE_BACKGROUND = "\u001B[45m";
|
||||
public static final String ANSI_CYAN_BACKGROUND = "\u001B[46m";
|
||||
public static final String ANSI_WHITE_BACKGROUND = "\u001B[47m";
|
||||
}
|
||||
18
src/quick/Global.java
Normal file
18
src/quick/Global.java
Normal file
@ -0,0 +1,18 @@
|
||||
package quick;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Global {
|
||||
private static Global instance;
|
||||
public List<Class<? extends View>> registeredViews = new ArrayList<>();
|
||||
|
||||
private Global() {}
|
||||
|
||||
public static synchronized Global getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new Global();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
7
src/quick/Helper.java
Normal file
7
src/quick/Helper.java
Normal file
@ -0,0 +1,7 @@
|
||||
package quick;
|
||||
|
||||
public class Helper {
|
||||
public static void clearScreen() {
|
||||
System.out.print("[H[2J[3J");
|
||||
}
|
||||
}
|
||||
99
src/quick/Interface.java
Normal file
99
src/quick/Interface.java
Normal file
@ -0,0 +1,99 @@
|
||||
package quick;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
import quick.exceptions.InvalidViewActionReturn;
|
||||
import quick.guard.PrintDog;
|
||||
import quick.intern.v_help;
|
||||
import quick.intern.v_select;
|
||||
|
||||
public class Interface {
|
||||
public final Scanner scanner = new Scanner(System.in);
|
||||
private Global global = Global.getInstance();
|
||||
private Boolean active = true;
|
||||
private Class<? extends View> defaultView = v_select.class;
|
||||
private View selectedView;
|
||||
public int[] __terminalSize;
|
||||
public int __renderCycleLines;
|
||||
|
||||
public Interface() {}
|
||||
|
||||
public Interface(Class<? extends View> defaultView) {
|
||||
this.defaultView = defaultView;
|
||||
}
|
||||
|
||||
public void registerView(Class<? extends View> viewClass) {
|
||||
global.registeredViews.add(viewClass);
|
||||
}
|
||||
|
||||
public Boolean selectView(View viewInstance) {
|
||||
selectedView = viewInstance;
|
||||
return viewInstance.init();
|
||||
}
|
||||
|
||||
public Boolean selectView(Class<? extends View> viewClass) {
|
||||
View new_view = View.instantiate(viewClass);
|
||||
selectedView = new_view;
|
||||
return new_view.init();
|
||||
}
|
||||
|
||||
private Boolean cycle() {
|
||||
selectedView.initBase(this);
|
||||
|
||||
// Always start with a clean break
|
||||
System.out.print("\r\n");
|
||||
System.out.flush();
|
||||
|
||||
this.__terminalSize = TerminalSize.getTerminalSize();
|
||||
this.__renderCycleLines = 0;
|
||||
|
||||
Helper.clearScreen();
|
||||
|
||||
// Draw the view; it should update __renderCycleLines
|
||||
selectedView.draw();
|
||||
System.out.flush();
|
||||
|
||||
// Always leave at least 2 lines: one for prompt, one for safety
|
||||
int overheadLines = 2;
|
||||
int linesToFill = Math.max(0, this.__terminalSize[0] - __renderCycleLines - overheadLines);
|
||||
|
||||
for (int i = 0; i < linesToFill; i++) {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
String view_prompt = (selectedView.viewPrompt == null ? "" : selectedView.viewPrompt);
|
||||
System.err.print(view_prompt);
|
||||
System.err.flush();
|
||||
String userInputRaw = scanner.nextLine();
|
||||
System.out.println();
|
||||
|
||||
Action viewResponse = selectedView.onCommand(userInputRaw);
|
||||
|
||||
if (viewResponse == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (viewResponse instanceof Action) {
|
||||
return viewResponse.execute(this);
|
||||
}
|
||||
|
||||
throw new InvalidViewActionReturn();
|
||||
}
|
||||
|
||||
private void mainLoop() {
|
||||
selectView(defaultView);
|
||||
|
||||
while (active) {
|
||||
active = cycle();
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
PrintDog.start(true);
|
||||
View.scanner = scanner;
|
||||
registerView(v_help.class);
|
||||
registerView(v_select.class);
|
||||
|
||||
mainLoop();
|
||||
}
|
||||
}
|
||||
25
src/quick/TerminalSize.java
Normal file
25
src/quick/TerminalSize.java
Normal file
@ -0,0 +1,25 @@
|
||||
package quick;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class TerminalSize {
|
||||
public static int[] getTerminalSize() {
|
||||
try {
|
||||
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", "stty size < /dev/tty"});
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line = reader.readLine();
|
||||
|
||||
if (line != null) {
|
||||
String[] parts = line.trim().split(" ");
|
||||
int rows = Integer.parseInt(parts[0]);
|
||||
int cols = Integer.parseInt(parts[1]);
|
||||
return new int[]{rows, cols};
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("[ERROR] Failed to get terminal size: " + e.getMessage());
|
||||
}
|
||||
|
||||
return new int[]{69, 69}; // Soggy UwU fallback
|
||||
}
|
||||
}
|
||||
53
src/quick/View.java
Normal file
53
src/quick/View.java
Normal file
@ -0,0 +1,53 @@
|
||||
package quick;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
import quick.exceptions.DuplicateViewMatchException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public abstract class View {
|
||||
protected static Global global = Global.getInstance();
|
||||
protected static ViewActionFactories actions = new ViewActionFactories();
|
||||
protected ViewTerminalAcces terminal;
|
||||
public static Scanner scanner;
|
||||
public static Interface __base;
|
||||
public String viewPrompt;
|
||||
public String viewSignature;
|
||||
public String helperText;
|
||||
|
||||
public abstract Boolean init();
|
||||
public abstract void draw();
|
||||
public abstract Action onCommand(String command);
|
||||
public abstract Boolean onSelection(String userInputRaw);
|
||||
|
||||
public void initBase(Interface base) {
|
||||
View.__base = base;
|
||||
this.terminal = new ViewTerminalAcces(base);
|
||||
}
|
||||
|
||||
public static View instantiate(Class<? extends View> viewClass) {
|
||||
try {
|
||||
return viewClass.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not instantiate view: " + viewClass.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static View signatureParse(String userInputRaw) {
|
||||
ArrayList<View> matched = new ArrayList<View>();
|
||||
|
||||
for (Class<? extends View> ViewClass : global.registeredViews) {
|
||||
View viewInstance = View.instantiate(ViewClass);
|
||||
if (viewInstance.onSelection(userInputRaw)) {
|
||||
matched.add(viewInstance);
|
||||
}
|
||||
}
|
||||
|
||||
if (matched.size() > 1) {
|
||||
throw new DuplicateViewMatchException(userInputRaw, matched.size());
|
||||
}
|
||||
|
||||
return matched.isEmpty() ? null : matched.get(0);
|
||||
}
|
||||
}
|
||||
28
src/quick/ViewActionFactories.java
Normal file
28
src/quick/ViewActionFactories.java
Normal file
@ -0,0 +1,28 @@
|
||||
package quick;
|
||||
|
||||
import quick.action.a_dd;
|
||||
import quick.action.a_nop;
|
||||
import quick.action.a_redirect;
|
||||
import quick.action.a_stop;
|
||||
|
||||
public class ViewActionFactories {
|
||||
public Action redirect(Class<? extends View> target) {
|
||||
return new a_redirect(target);
|
||||
}
|
||||
|
||||
public Action redirect(View target) {
|
||||
return new a_redirect(target);
|
||||
}
|
||||
|
||||
public Action stop() {
|
||||
return new a_stop();
|
||||
}
|
||||
|
||||
public Action nop() {
|
||||
return new a_nop();
|
||||
}
|
||||
|
||||
public Action dd(Object... data) {
|
||||
return new a_dd(data);
|
||||
}
|
||||
}
|
||||
32
src/quick/ViewTerminalAcces.java
Normal file
32
src/quick/ViewTerminalAcces.java
Normal file
@ -0,0 +1,32 @@
|
||||
package quick;
|
||||
|
||||
public class ViewTerminalAcces {
|
||||
private Interface base;
|
||||
|
||||
public ViewTerminalAcces(Interface base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
public void print(String text) {
|
||||
base.__renderCycleLines += 1;
|
||||
System.out.print(text);
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return base.__terminalSize[1];
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return base.__terminalSize[0];
|
||||
}
|
||||
|
||||
public int getCurrentRclCount() {
|
||||
return base.__renderCycleLines;
|
||||
}
|
||||
|
||||
public void fill(int size) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
print("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
109
src/quick/action/a_dd.java
Normal file
109
src/quick/action/a_dd.java
Normal file
@ -0,0 +1,109 @@
|
||||
package quick.action;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import quick.Action;
|
||||
import quick.Color;
|
||||
import quick.Interface;
|
||||
|
||||
public class a_dd extends Action {
|
||||
private final Object[] data;
|
||||
|
||||
public a_dd(Object... data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean execute(Interface base) {
|
||||
System.out.println(Color.ANSI_BLUE + "========== DUMP & DIE ==========" + Color.ANSI_RESET);
|
||||
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
Object item = data[i];
|
||||
String type = (item == null) ? "null" : item.getClass().getSimpleName();
|
||||
String typeColor = getTypeColor(type);
|
||||
|
||||
System.out.println(
|
||||
Color.ANSI_WHITE + "[" + i + "] => " +
|
||||
typeColor + "(" + type + ") " + formatValue(item) + Color.ANSI_RESET
|
||||
);
|
||||
}
|
||||
|
||||
System.out.println(Color.ANSI_BLUE + "============ END ============" + Color.ANSI_RESET);
|
||||
return false;
|
||||
}
|
||||
|
||||
private String formatValue(Object obj) {
|
||||
if (obj == null) return Color.ANSI_RED + "null" + Color.ANSI_RESET;
|
||||
|
||||
if (obj instanceof Map) {
|
||||
StringBuilder sb = new StringBuilder(Color.ANSI_PURPLE + "Map {\n");
|
||||
((Map<?, ?>) obj).forEach((k, v) -> {
|
||||
String type = (v == null) ? "null" : v.getClass().getSimpleName();
|
||||
String color = getTypeColor(type);
|
||||
sb.append(Color.ANSI_WHITE + " " + k + " => " + color + "(" + type + ") " + formatValue(v) + "\n");
|
||||
});
|
||||
sb.append(Color.ANSI_PURPLE + "}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
if (obj instanceof List) {
|
||||
StringBuilder sb = new StringBuilder(Color.ANSI_BLUE + "List [\n");
|
||||
int i = 0;
|
||||
for (Object val : (List<?>) obj) {
|
||||
String type = (val == null) ? "null" : val.getClass().getSimpleName();
|
||||
String color = getTypeColor(type);
|
||||
sb.append(Color.ANSI_WHITE + " " + i++ + " => " + color + "(" + type + ") " + formatValue(val) + "\n");
|
||||
}
|
||||
sb.append(Color.ANSI_BLUE + "]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
if (obj instanceof Set) {
|
||||
StringBuilder sb = new StringBuilder(Color.ANSI_RED + "Set {\n");
|
||||
for (Object val : (Set<?>) obj) {
|
||||
String type = (val == null) ? "null" : val.getClass().getSimpleName();
|
||||
String color = getTypeColor(type);
|
||||
sb.append(Color.ANSI_WHITE + " => " + color + "(" + type + ") " + formatValue(val) + "\n");
|
||||
}
|
||||
sb.append(Color.ANSI_RED + "}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
if (obj.getClass().isArray()) {
|
||||
StringBuilder sb = new StringBuilder(Color.ANSI_CYAN + "Array [\n");
|
||||
int len = java.lang.reflect.Array.getLength(obj);
|
||||
for (int i = 0; i < len; i++) {
|
||||
Object val = java.lang.reflect.Array.get(obj, i);
|
||||
String type = (val == null) ? "null" : val.getClass().getSimpleName();
|
||||
String color = getTypeColor(type);
|
||||
sb.append(Color.ANSI_WHITE + " " + i + " => " + color + "(" + type + ") " + formatValue(val) + "\n");
|
||||
}
|
||||
sb.append(Color.ANSI_CYAN + "]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
return getTypeColor(obj.getClass().getSimpleName()) + obj.toString();
|
||||
}
|
||||
|
||||
private String getTypeColor(String type) {
|
||||
if (type == null) return Color.ANSI_RED;
|
||||
|
||||
switch (type) {
|
||||
case "String": return Color.ANSI_GREEN;
|
||||
case "Integer":
|
||||
case "Long":
|
||||
case "Double":
|
||||
case "Float":
|
||||
case "Short":
|
||||
case "Byte": return Color.ANSI_YELLOW;
|
||||
case "Boolean": return Color.ANSI_CYAN;
|
||||
case "Map": return Color.ANSI_PURPLE;
|
||||
case "List": return Color.ANSI_BLUE;
|
||||
case "Set": return Color.ANSI_RED;
|
||||
case "Object[]": return Color.ANSI_WHITE;
|
||||
default: return Color.ANSI_WHITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/quick/action/a_nop.java
Normal file
10
src/quick/action/a_nop.java
Normal file
@ -0,0 +1,10 @@
|
||||
package quick.action;
|
||||
|
||||
import quick.Action;
|
||||
import quick.Interface;
|
||||
|
||||
public class a_nop extends Action {
|
||||
public Boolean execute(Interface base) {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
49
src/quick/action/a_redirect.java
Normal file
49
src/quick/action/a_redirect.java
Normal file
@ -0,0 +1,49 @@
|
||||
package quick.action;
|
||||
|
||||
import quick.Action;
|
||||
import quick.Interface;
|
||||
import quick.View;
|
||||
import quick.exceptions.InvalidRedirect;
|
||||
|
||||
public class a_redirect extends Action{
|
||||
public Class<? extends View> targetClass;
|
||||
public View targetInstance;
|
||||
private Interface base;
|
||||
|
||||
public a_redirect(Class<? extends View> target) {
|
||||
this.targetClass = target;
|
||||
}
|
||||
|
||||
public a_redirect(View target) {
|
||||
this.targetInstance = target;
|
||||
}
|
||||
|
||||
public Boolean execute(Interface base) {
|
||||
this.base = base;
|
||||
|
||||
if (targetClass != null) {
|
||||
return redirectByClass();
|
||||
}
|
||||
if (targetInstance != null) {
|
||||
return redirectByInstance();
|
||||
}
|
||||
|
||||
throw new InvalidRedirect(null);
|
||||
}
|
||||
|
||||
private Boolean redirectByClass() {
|
||||
if (!global.registeredViews.contains(targetClass)) {
|
||||
throw new InvalidRedirect(targetClass);
|
||||
}
|
||||
|
||||
return base.selectView(targetClass);
|
||||
}
|
||||
|
||||
private Boolean redirectByInstance() {
|
||||
if (!global.registeredViews.contains(targetInstance.getClass())) {
|
||||
throw new InvalidRedirect(targetInstance.getClass());
|
||||
}
|
||||
|
||||
return base.selectView(targetInstance);
|
||||
}
|
||||
}
|
||||
10
src/quick/action/a_stop.java
Normal file
10
src/quick/action/a_stop.java
Normal file
@ -0,0 +1,10 @@
|
||||
package quick.action;
|
||||
|
||||
import quick.Action;
|
||||
import quick.Interface;
|
||||
|
||||
public class a_stop extends Action {
|
||||
public Boolean execute(Interface base) {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
7
src/quick/exceptions/DuplicateViewMatchException.java
Normal file
7
src/quick/exceptions/DuplicateViewMatchException.java
Normal file
@ -0,0 +1,7 @@
|
||||
package quick.exceptions;
|
||||
|
||||
public class DuplicateViewMatchException extends RuntimeException {
|
||||
public DuplicateViewMatchException(String input, int count) {
|
||||
super("Input \"" + input + "\" matched " + count + " views. View signatures must be unique for this input.");
|
||||
}
|
||||
}
|
||||
7
src/quick/exceptions/IllegalTerminalPrintException.java
Normal file
7
src/quick/exceptions/IllegalTerminalPrintException.java
Normal file
@ -0,0 +1,7 @@
|
||||
package quick.exceptions;
|
||||
|
||||
public class IllegalTerminalPrintException extends RuntimeException {
|
||||
public IllegalTerminalPrintException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
11
src/quick/exceptions/InvalidRedirect.java
Normal file
11
src/quick/exceptions/InvalidRedirect.java
Normal file
@ -0,0 +1,11 @@
|
||||
package quick.exceptions;
|
||||
|
||||
import quick.View;
|
||||
|
||||
public class InvalidRedirect extends RuntimeException {
|
||||
public InvalidRedirect(Class<? extends View> target) {
|
||||
super(target == null
|
||||
? "invalid use of null in redirect"
|
||||
: "View" + target.getName() + " is not registered in base");
|
||||
}
|
||||
}
|
||||
7
src/quick/exceptions/InvalidViewActionReturn.java
Normal file
7
src/quick/exceptions/InvalidViewActionReturn.java
Normal file
@ -0,0 +1,7 @@
|
||||
package quick.exceptions;
|
||||
|
||||
public class InvalidViewActionReturn extends RuntimeException {
|
||||
public InvalidViewActionReturn() {
|
||||
super("Please only return a Child of Action or null from a View");
|
||||
}
|
||||
}
|
||||
101
src/quick/guard/PrintDog.java
Normal file
101
src/quick/guard/PrintDog.java
Normal file
@ -0,0 +1,101 @@
|
||||
package quick.guard;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Set;
|
||||
|
||||
import quick.Color;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
public class PrintDog {
|
||||
private static final Set<String> trustedPackages = new HashSet<>();
|
||||
private static boolean strict = true;
|
||||
private static PrintStream originalOut;
|
||||
private static PrintStream mirroredOut;
|
||||
|
||||
public static void start(boolean strictMode) {
|
||||
strict = strictMode;
|
||||
|
||||
originalOut = System.out;
|
||||
|
||||
// Register your actual source packages only
|
||||
trustedPackages.add("quick.");
|
||||
trustedPackages.add("quick.action.");
|
||||
trustedPackages.add("quick.exceptions.");
|
||||
trustedPackages.add("quick.guard.");
|
||||
trustedPackages.add("quick.intern.");
|
||||
|
||||
// Setup global uncaught exception handler
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
||||
originalOut.println(Color.ANSI_RED + "🔥 UNCAUGHT EXCEPTION IN THREAD: " + thread.getName() + Color.ANSI_RESET);
|
||||
throwable.printStackTrace(originalOut);
|
||||
// Optional kill:
|
||||
// System.exit(1);
|
||||
});
|
||||
|
||||
// Setup mirror output stream
|
||||
mirroredOut = new PrintStream(new MirrorOutputStream(originalOut), true);
|
||||
System.setOut(mirroredOut);
|
||||
}
|
||||
|
||||
private static class MirrorOutputStream extends OutputStream {
|
||||
private final OutputStream original;
|
||||
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
|
||||
public MirrorOutputStream(OutputStream original) {
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) {
|
||||
try {
|
||||
original.write(b);
|
||||
buffer.write(b);
|
||||
|
||||
if (b == '\n') {
|
||||
flushBuffer();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(originalOut);
|
||||
System.exit(69);
|
||||
}
|
||||
}
|
||||
|
||||
private void flushBuffer() {
|
||||
String content = buffer.toString();
|
||||
buffer.reset();
|
||||
|
||||
Throwable t = new Throwable();
|
||||
StackTraceElement[] stack = t.getStackTrace();
|
||||
|
||||
if (strict && !isFromTrustedCode(stack)) {
|
||||
originalOut.println(
|
||||
Color.ANSI_WHITE + Color.ANSI_RED_BACKGROUND +
|
||||
"🐶 BARK! UNAUTHORIZED TERMINAL PRINT DETECTED!" +
|
||||
Color.ANSI_RESET + "\n" +
|
||||
Color.ANSI_YELLOW + "→ Use terminal.print() instead.\n" +
|
||||
"→ Offending content:\n" +
|
||||
Color.ANSI_WHITE + content.trim() + Color.ANSI_RESET
|
||||
);
|
||||
|
||||
// 🚨 HARD CRASH
|
||||
System.err.println(Color.ANSI_RED + "☠️ FATAL: System.out used in untrusted context!" + Color.ANSI_RESET);
|
||||
System.exit(123);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFromTrustedCode(StackTraceElement[] stack) {
|
||||
for (StackTraceElement frame : stack) {
|
||||
String cls = frame.getClassName();
|
||||
for (String prefix : trustedPackages) {
|
||||
if (cls.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/quick/intern/v_help.java
Normal file
30
src/quick/intern/v_help.java
Normal file
@ -0,0 +1,30 @@
|
||||
package quick.intern;
|
||||
|
||||
import quick.Action;
|
||||
import quick.View;
|
||||
|
||||
public class v_help extends View {
|
||||
@Override
|
||||
public Boolean init() {
|
||||
viewPrompt = "HELP";
|
||||
viewSignature = "help";
|
||||
helperText = "help view most likely not implemented 😂 u noob";
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action onCommand(String command) {
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean onSelection(String userInputRaw) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
68
src/quick/intern/v_select.java
Normal file
68
src/quick/intern/v_select.java
Normal file
@ -0,0 +1,68 @@
|
||||
package quick.intern;
|
||||
|
||||
import quick.Action;
|
||||
import quick.Color;
|
||||
import quick.View;
|
||||
import quick.action.a_nop;
|
||||
import quick.action.a_redirect;
|
||||
|
||||
public class v_select extends View {
|
||||
private String viewList;
|
||||
private String lastSelectionWrong;
|
||||
|
||||
@Override
|
||||
public Boolean init() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Class<? extends View> viewClass : global.registeredViews) {
|
||||
if (viewClass == v_select.class) continue;
|
||||
View viewInstance = View.instantiate(viewClass);
|
||||
viewInstance.init();
|
||||
if (viewInstance.viewSignature != null && !viewInstance.viewSignature.isEmpty()) {
|
||||
sb.append(": " + viewInstance.viewSignature + " - " + viewInstance.helperText).append("\n");
|
||||
} else {
|
||||
sb.append(": " + viewInstance.helperText).append("\n");
|
||||
}
|
||||
}
|
||||
if (sb.length() > 0 && sb.charAt(sb.length() - 1) == '\n') {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
}
|
||||
viewList = sb.toString();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
terminal.print("Select a action:\n");
|
||||
for (String line : viewList.split("\n")) terminal.print(line+"\n");
|
||||
terminal.print("\n");
|
||||
|
||||
if (lastSelectionWrong != null) {
|
||||
terminal.print(Color.ANSI_RED + "Something went wrong with: " + lastSelectionWrong + "\"" + Color.ANSI_RESET + "\n");
|
||||
terminal.print("\n");
|
||||
lastSelectionWrong = null;
|
||||
} else {
|
||||
terminal.print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action onCommand(String command) {
|
||||
if (command.isEmpty()) {
|
||||
return new a_nop();
|
||||
}
|
||||
|
||||
View selectedView = View.signatureParse(command);
|
||||
|
||||
if (selectedView == null) {
|
||||
lastSelectionWrong = command;
|
||||
return new a_nop();
|
||||
}
|
||||
|
||||
return new a_redirect(selectedView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean onSelection(String userInputRaw) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,21 @@
|
||||
package src.models.squirrel;
|
||||
package squirrel;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Database {
|
||||
private static Connection conn;
|
||||
public static Consumer<Connection> migrate;
|
||||
|
||||
public static Connection getConnection() throws SQLException {
|
||||
if (conn == null || conn.isClosed()) {
|
||||
String url = "jdbc:sqlite:instance/test.db";
|
||||
conn = DriverManager.getConnection(url);
|
||||
if (migrate != null) {
|
||||
migrate.accept(conn);
|
||||
public static Connection getConnection() {
|
||||
try {
|
||||
if (conn == null || conn.isClosed()) {
|
||||
String url = "jdbc:sqlite:instance/test.db";
|
||||
conn = DriverManager.getConnection(url);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Failed to connect to database", e);
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
95
src/squirrel/LocalStorage.java
Normal file
95
src/squirrel/LocalStorage.java
Normal file
@ -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 <T> void save(String key, T value) {
|
||||
try {
|
||||
var ctor = value.getClass().getDeclaredConstructors()[0];
|
||||
ctor.setAccessible(true);
|
||||
|
||||
var fields = value.getClass().getDeclaredFields();
|
||||
List<String> 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> T load(String key, Class<T> 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<String, String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package src.models.squirrel;
|
||||
package squirrel;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
@ -10,18 +10,45 @@ public abstract class Model {
|
||||
protected Set<String> columns = new HashSet<>();
|
||||
protected Map<String, Object> attributes = new LinkedHashMap<>();
|
||||
|
||||
protected boolean rowMode = false;
|
||||
protected boolean rowMode = true;
|
||||
|
||||
// Only one classmode instance per subclass
|
||||
private static final Map<Class<? extends Model>, Model> classModeInstances = new HashMap<>();
|
||||
|
||||
public Model() {
|
||||
if (conn == null) {
|
||||
conn = Database.getConnection();
|
||||
}
|
||||
// Always rowMode by default
|
||||
this.rowMode = true;
|
||||
}
|
||||
|
||||
// Call once per subclass to create the classmode instance
|
||||
public static <T extends Model> T initializeClassMode(Class<T> clazz) {
|
||||
synchronized (classModeInstances) {
|
||||
if (classModeInstances.containsKey(clazz)) {
|
||||
throw new IllegalStateException("Classmode instance already initialized for " + clazz.getSimpleName());
|
||||
}
|
||||
try {
|
||||
conn = Database.getConnection();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException("Failed to get DB connection for model '" + getTableName() + "'", e);
|
||||
T instance = clazz.getDeclaredConstructor().newInstance();
|
||||
instance.rowMode = false;
|
||||
classModeInstances.put(clazz, instance);
|
||||
return instance;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to initialize classmode instance for " + clazz.getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the classmode instance for a subclass
|
||||
public static <T extends Model> T getClassMode(Class<T> clazz) {
|
||||
Model instance = classModeInstances.get(clazz);
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException("Classmode instance not initialized for " + clazz.getSimpleName());
|
||||
}
|
||||
return clazz.cast(instance);
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Query (class/global mode only)
|
||||
// -------------------------------
|
||||
@ -1,8 +1,8 @@
|
||||
package src.models.squirrel;
|
||||
package squirrel;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import src.models.UserModel;
|
||||
import models.UserModel;
|
||||
|
||||
public class ModelManager {
|
||||
private static final Map<Class<? extends Model>, Model> models = new HashMap<>();
|
||||
@ -13,7 +13,7 @@ public class ModelManager {
|
||||
);
|
||||
for (Class<? extends Model> clazz : modelClasses) {
|
||||
try {
|
||||
Model instance = clazz.getDeclaredConstructor().newInstance();
|
||||
Model instance = Model.initializeClassMode(clazz);
|
||||
models.put(clazz, instance);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Loading…
x
Reference in New Issue
Block a user