Browse Source

Added Database I/O

BlackPhreak 6 years ago
parent
commit
a80c0f8bd9

+ 5 - 2
src/me/blackphreak/CommandHandling/AbstractCommandHandler.java

@@ -7,6 +7,9 @@ public abstract class AbstractCommandHandler {
 		this.description = desc;
 	}
 	
-	public abstract void handle();
-	protected abstract void doneAndReset();
+	/**
+	 * Command handling
+	 * @return true = back to main menu, false = end program
+	 */
+	public abstract boolean handle();
 }

+ 29 - 14
src/me/blackphreak/CommandHandling/CommandHandler.java

@@ -1,30 +1,28 @@
 package me.blackphreak.CommandHandling;
 
-import me.blackphreak.Debug.Debug;
-
 import java.util.HashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static me.blackphreak.Lib.*;
+import static me.blackphreak.Lib.promptQuestion;
+import static me.blackphreak.Lib.tryParseUInt;
 
 public class CommandHandler {
-	// use hashmap to remove duplicated handlers
+	// use hashmap to remove duplicated handlers if any
 	private static HashMap</*cmd*/String, AbstractCommandHandler> commandMap = new HashMap<>();
 	
 	static void registerHandler(String cmd, AbstractCommandHandler handler) {
 		commandMap.put(cmd, handler);
 	}
 	
-	/*public static void deregisterHandler(String cmd) {
-		commandMap.remove(cmd);
-	}*/
-	
+	/**
+	 * print the option menu, nothing special
+	 */
 	public static void printOptions() {
 		String inp;
 		int chosen;
 		
 		do {
-			System.out.println("Actions:");
+			System.out.println("\nActions:");
 			
 			AtomicInteger i = new AtomicInteger();
 			commandMap.forEach((cmd, handler) -> System.out.println(String.format(
@@ -34,6 +32,14 @@ public class CommandHandler {
 					handler.description
 			)));
 			
+			// exit option
+			System.out.println(String.format(
+					"  %2s. %-15s %s",
+					"99",
+					"Exit",
+					"Close this application"
+			));
+			
 			inp = promptQuestion(String.format(
 					"Please select an action (1 - %d): ",
 					commandMap.size()
@@ -48,6 +54,12 @@ public class CommandHandler {
 			if (chosen <= 0
 					|| chosen > commandMap.size())
 			{
+				if (chosen == 99)
+				{
+					// exit
+					System.out.println("Exiting... Bye~");
+					break;
+				}
 				System.out.println("Invalid action. (Must in between 1 to "+(commandMap.size())+", inclusive)");
 				chosen = -1;  // keep the loop
 			}
@@ -58,10 +70,13 @@ public class CommandHandler {
 				chosen--;
 			}
 			
-			// loop until a valid option is picked. (chosen != -1)
-		} while (chosen == -1);
-		
-		// pass to the command handler for rest of the actions.
-		((AbstractCommandHandler)(commandMap.values().toArray()[chosen])).handle();
+			if (chosen == -1)
+				continue;
+			
+			// pass to the command handler for rest of the actions.
+			if (!((AbstractCommandHandler)(commandMap.values().toArray()[chosen])).handle())
+				break;  // exit
+			
+		} while (true);
 	}
 }

+ 5 - 3
src/me/blackphreak/CommandHandling/CommandManager.java

@@ -1,10 +1,12 @@
 package me.blackphreak.CommandHandling;
 
-import me.blackphreak.CommandHandling.Handlers.*;
+import me.blackphreak.CommandHandling.Handlers.CreateStudentCommand;
+import me.blackphreak.CommandHandling.Handlers.SearchStudentCommand;
 
 public class CommandManager {
 	public static void registerCommands() {
-		CommandHandler.registerHandler("Search", new SearchCommand("Search student(s) from database with condition(s)"));
-		CommandHandler.registerHandler("Create", new CreateCommand("Insert a new student record to database"));
+		CommandHandler.registerHandler("Search Student",    new SearchStudentCommand("Search student(s) from database with condition(s)"));
+		CommandHandler.registerHandler("Create Student",    new CreateStudentCommand("Insert a new student record to database"));
+		CommandHandler.registerHandler("Edit Student",      new EditStudentCommand("Modify the information of a selected student"));
 	}
 }

+ 13 - 0
src/me/blackphreak/CommandHandling/EditStudentCommand.java

@@ -0,0 +1,13 @@
+package me.blackphreak.CommandHandling;
+
+public class EditStudentCommand extends AbstractCommandHandler {
+	public EditStudentCommand(String desc) {
+		super(desc);
+	}
+	
+	@Override
+	public boolean handle() {
+		// TODO: whatever la, but not today do lo :(
+		return false;
+	}
+}

+ 73 - 23
src/me/blackphreak/CommandHandling/Handlers/CreateCommand.java → src/me/blackphreak/CommandHandling/Handlers/CreateStudentCommand.java

@@ -1,19 +1,21 @@
 package me.blackphreak.CommandHandling.Handlers;
 
 import me.blackphreak.CommandHandling.AbstractCommandHandler;
+import me.blackphreak.Database;
 import me.blackphreak.Debug.Debug;
 import me.blackphreak.Student.AbstractStudent;
-import me.blackphreak.Student.Types.*;
+import me.blackphreak.Student.Types.LocalStudent;
+import me.blackphreak.Student.Types.OverseaStudent;
 
-import static me.blackphreak.Lib.*;
+import static me.blackphreak.Lib.promptQuestion;
 
-public class CreateCommand extends AbstractCommandHandler {
-	public CreateCommand(String desc) {
+public class CreateStudentCommand extends AbstractCommandHandler {
+	public CreateStudentCommand(String desc) {
 		super(desc);
 	}
 	
 	@Override
-	public void handle() {
+	public boolean handle() {
 		AbstractStudent stu = null;
 		
 		do {
@@ -31,8 +33,11 @@ public class CreateCommand extends AbstractCommandHandler {
 		} while (stu == null);
 		
 		System.out.println("  Please provide information about the student:");
+		// prompt special question first
+		stu.promptQuestionEx();
+		
 		do {
-			var inp = promptQuestion("    Chinese Name: ");
+			var inp = promptQuestion("    Chinese Name (allow empty): ", false);
 			
 			if (stu instanceof LocalStudent)
 			{
@@ -41,12 +46,13 @@ public class CreateCommand extends AbstractCommandHandler {
 				else
 					stu.setChtName(inp);
 			}
-			else
+			else if (stu instanceof OverseaStudent)
 				stu.setChtName("");
+			
 		} while (stu.getChtName() == null);
 		
 		do {
-			var inp = promptQuestion("    English Name: ");
+			var inp = promptQuestion("    English Name: ", false);
 			
 			if (inp.isEmpty() || inp.isBlank())
 				System.out.println("Invalid. Please try again.");
@@ -55,7 +61,7 @@ public class CreateCommand extends AbstractCommandHandler {
 		} while (stu.getEngName() == null);
 		
 		do {
-			var inp = promptQuestion("    Home Address: ");
+			var inp = promptQuestion("    Home Address: ", false);
 			
 			if (inp.isEmpty() || inp.isBlank())
 				System.out.println("Invalid. Please try again.");
@@ -64,7 +70,7 @@ public class CreateCommand extends AbstractCommandHandler {
 		} while (stu.getHomeAddress() == null);
 		
 		do {
-			var inp = promptQuestion("    Mobile Number: ");
+			var inp = promptQuestion("    Mobile Number: ", false);
 			
 			if (inp.isEmpty()
 					|| inp.isBlank()
@@ -75,7 +81,7 @@ public class CreateCommand extends AbstractCommandHandler {
 		} while (stu.getMobileNumber() == null);
 		
 		do {
-			var inp = promptQuestion("    Nationality: ")
+			var inp = promptQuestion("    Nationality: ", false)
 					.toUpperCase();
 			
 			if (inp.isEmpty()
@@ -86,7 +92,7 @@ public class CreateCommand extends AbstractCommandHandler {
 		} while (stu.getNationality() == null);
 		
 		do {
-			var inp = promptQuestion("    Current Semester: ");
+			var inp = promptQuestion("    Current Semester: ", false);
 			
 			if (inp.isEmpty()
 					|| inp.isBlank()
@@ -94,7 +100,7 @@ public class CreateCommand extends AbstractCommandHandler {
 				System.out.println("Invalid. Please try again.");
 			else
 				stu.setSemester(Integer.parseInt(inp));
-		} while (stu.getSemester() == 0);
+		} while (stu.getSemester() <= 0);
 		
 		String tryAgain;
 		do {
@@ -120,20 +126,64 @@ public class CreateCommand extends AbstractCommandHandler {
 		} while (tryAgain.isEmpty() || tryAgain.equalsIgnoreCase("Y"));
 		
 		if (stu.getStudentID().isEmpty()) {
-			Debug.err("Failed to allocate studentID. Exiting..");
-			System.exit(1);
-			return;
+			Debug.err("Failed to allocate studentID. Back to main menu.");
+			return true;
 		}
 		
 		Debug.info("Student["+stu.getStudentID()+"] created. Saving to database...");
 		
-		// TODO: save to db
+		// save to db (those SQL injection attack, I don't give a f___ in this project)
+		var sb = new StringBuilder("INSERT INTO Student (StudentID, ChtName, EngName, Address, MobileNum, Nationality, ");
+		String sql;
 		
-		Debug.info("Success. Exiting entry mode.");
-	}
-	
-	@Override
-	protected void doneAndReset() {
-	
+		if (stu instanceof LocalStudent) {
+			var tmpStu = (LocalStudent)stu;
+			
+			//StudentID, ChtName, EngName, Address, MobileNum, Nationality, HKID
+			sb.append("HKID) VALUES (\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\");");
+			
+			sql = String.format(
+					sb.toString(),
+					tmpStu.getStudentID(),
+					tmpStu.getChtName(),
+					tmpStu.getEngName(),
+					tmpStu.getHomeAddress(),
+					tmpStu.getMobileNumber(),
+					tmpStu.getNationality(),
+					tmpStu.getHKID()
+			);
+		}
+		else if (stu instanceof OverseaStudent) {
+			var tmpStu = (OverseaStudent)stu;
+			
+			//StudentID, ChtName, EngName, Address, MobileNum, Nationality, VISA
+			sb.append("VISA) VALUES (\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\");");
+			
+			sql = String.format(
+					sb.toString(),
+					tmpStu.getStudentID(),
+					tmpStu.getChtName() == null || tmpStu.getChtName().isEmpty()
+							? null
+							: tmpStu.getChtName(),
+					tmpStu.getEngName(),
+					tmpStu.getHomeAddress(),
+					tmpStu.getMobileNumber(),
+					tmpStu.getNationality(),
+					tmpStu.getVisa()
+			);
+		}
+		else
+		{
+			Debug.err("Unknown student type.");
+			return true;
+		}
+		
+		// obj to json
+		System.out.println("** Lazy creation: "+stu.toJson()+" **");
+		
+		Database.getInstance().update(sql);
+		
+		Debug.info("Success. Returning to main menu.");
+		return true;
 	}
 }

+ 86 - 83
src/me/blackphreak/CommandHandling/Handlers/SearchCommand.java → src/me/blackphreak/CommandHandling/Handlers/SearchStudentCommand.java

@@ -9,17 +9,20 @@ import me.blackphreak.Student.Types.OverseaStudent;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import static me.blackphreak.Lib.promptQuestion;
 import static me.blackphreak.Lib.tryParseUInt;
 
-public class SearchCommand extends AbstractCommandHandler {
+public class SearchStudentCommand extends AbstractCommandHandler {
 	private List<String> colNames = new ArrayList<>();
-	private HashMap</*colName*/String, /*searchCondition*/String> selectedCols = new HashMap<>();
+	private HashMap</*colName*/String, /*searchCondition*/String> selectedCols;
 	
-	public SearchCommand(String desc) {
+	public SearchStudentCommand(String desc) {
 		super(desc);
 		
 		Database db = Database.getInstance();
@@ -35,7 +38,7 @@ public class SearchCommand extends AbstractCommandHandler {
 	}
 	
 	@Override
-	public void handle() {
+	public boolean handle() {
 		boolean nextFlag;
 		do
 		{
@@ -45,14 +48,80 @@ public class SearchCommand extends AbstractCommandHandler {
 				System.out.println("Please select at least one column to search.");
 		} while (!nextFlag);
 		
+		
 		conditionEntryMenu();
 		
+		queryAndPrintMatchedStudent();
+		return true;
+	}
+	
+	private boolean selectColumnMenu() {
+		String inp;
+		
+		System.out.println("Search student by (multiple):");
+		
+		AtomicInteger i = new AtomicInteger();
+		// print keys only (display col name)
+		colNames.forEach(colName -> System.out.println(String.format(
+				"  %2s. %s",
+				i.incrementAndGet() + "",
+				colName
+		)));
+		
+		System.out.println("\n** use comma to separate columns **");
+		inp = promptQuestion(String.format(
+				"Select search column(s) (1 - %d): ",
+				colNames.size()
+		));
+		
+		// input validation & parsing
+		List<String> tmp_selectedCols = new ArrayList<>(Arrays.asList(
+				inp.replaceAll("\\s+", "")
+						.split(",")));
+		
+		// remove duplicated & validate column options
+		selectedCols = new HashMap<>();
+		tmp_selectedCols.stream()
+				.distinct()
+				.filter(targetCol ->
+						!targetCol.isBlank()
+								&& !targetCol.isEmpty()
+								&& targetCol.matches("\\d+")
+				)
+				.sorted()
+				.forEach(targetCol ->
+						selectedCols.put(colNames.get(
+								tryParseUInt(targetCol)-1
+						), ""));
+		
+		return selectedCols.size() > 0;
+	}
+	
+	private void conditionEntryMenu() {
+		selectedCols.keySet().forEach(col -> {
+			System.out.println("\n** use \"_\" as a wildcard character **");
+			System.out.println("** use \"%\" to represent any wildcard character(s) **");
+			System.out.println("** leave it blank to not consider as a condition **");
+			
+			String inp = promptQuestion(String.format(
+					"Search \"%s\" with: ",
+					col
+			));
+			
+			if (!inp.isEmpty() && !inp.isBlank())
+				selectedCols.put(col, inp);
+			else
+				selectedCols.remove(col);
+		});
+	}
+	
+	private void queryAndPrintMatchedStudent() {
 		// build SQL statement
 		StringBuilder sql = new StringBuilder("SELECT * FROM Student WHERE ");
 		AtomicInteger i = new AtomicInteger();
 		selectedCols.forEach((k, v) -> {
 			if (i.getAndIncrement() > 0) {
-				sql.append(" AND ");
+				sql.append(" OR ");
 			}
 			
 			sql.append("`").append(k).append("`");
@@ -95,8 +164,9 @@ public class SearchCommand extends AbstractCommandHandler {
 			} while (rs.next());
 		}
 		catch (SQLException e) {
-			// TODO: handling
+			Debug.err("Failed to search. Error:");
 			e.printStackTrace();
+			return;
 		}
 		
 		System.out.println(String.format(
@@ -116,15 +186,15 @@ public class SearchCommand extends AbstractCommandHandler {
 		
 		students.forEach((k, v) -> System.out.println(
 				String.format("+ %-13s | %-13s | %-13s | %-"+(20-v.getChtName().length())+"s | %-30s | %-10s | %s",
-					v.getStudentID(),
-					(v instanceof LocalStudent) ?
-							((LocalStudent)v).getHKID()
-							: ((OverseaStudent)v).getVisa(),
-					v.getNationality(),
-					v.getChtName(),
-					v.getEngName(),
-					v.getMobileNumber(),
-					v.getHomeAddress()
+						k,
+						(v instanceof LocalStudent)
+								? ((LocalStudent)v).getHKID()
+								: ((OverseaStudent)v).getVisa(),
+						v.getNationality(),
+						v.getChtName(),
+						v.getEngName(),
+						v.getMobileNumber(),
+						v.getHomeAddress()
 				)
 		));
 		
@@ -133,76 +203,9 @@ public class SearchCommand extends AbstractCommandHandler {
 		//                   ChtName              + EngName                        + mobile     + addr
 		System.out.println("----------------------+--------------------------------+------------+-->");
 		
-		
-		// TODO: allow selection
-	}
-	
-	private boolean selectColumnMenu() {
-		String inp;
-		
-		System.out.println("Search student by (multiple):");
-		
-		AtomicInteger i = new AtomicInteger();
-		// print keys only (display col name)
-		colNames.forEach(colName -> System.out.println(String.format(
-				"  %2s. %s",
-				i.incrementAndGet() + "",
-				colName
-		)));
-		
-		System.out.println("\n** use comma to separate columns **");
-		inp = promptQuestion(String.format(
-				"Select search column(s) (1 - %d): ",
-				colNames.size()
-		));
-		
-		// input validation & parsing
-		List<String> tmp_selectedCols = new ArrayList<>(Arrays.asList(
-				inp.replaceAll("\\s+", "")
-						.split(",")));
-		
-		// remove duplicated & validate column options
-		tmp_selectedCols.stream()
-				.distinct()
-				.filter(targetCol ->
-						!targetCol.isBlank()
-								&& !targetCol.isEmpty()
-								&& targetCol.matches("\\d+")
-				)
-				.sorted()
-				.forEach(targetCol ->
-						selectedCols.put(colNames.get(
-								tryParseUInt(targetCol)-1
-						), ""));
-		
-		return selectedCols.size() > 0;
-	}
-	
-	private void conditionEntryMenu() {
-		
-		selectedCols.keySet().forEach(col -> {
-			System.out.println("\n** use \"_\" as a wildcard character **");
-			System.out.println("** use \"%\" to represent any wildcard character(s) **");
-			System.out.println("** leave it blank to not consider as a condition **");
-			
-			String inp = promptQuestion(String.format(
-					"Search \"%s\" with: ",
-					col
-			));
-			
-			if (!inp.isEmpty() && !inp.isBlank())
-				selectedCols.put(col, inp);
-			else
-				selectedCols.remove(col);
-		});
 	}
 	
 	private void undo() {
 	
 	}
-	
-	@Override
-	protected void doneAndReset() {
-		selectedCols.clear();
-	}
 }

+ 27 - 0
src/me/blackphreak/Database.java

@@ -25,6 +25,9 @@ public class Database {
 			try {
 				Class.forName("org.sqlite.JDBC");
 				conn = DriverManager.getConnection("jdbc:sqlite:students.db");
+				
+				// disable auto-commit to boost performance (Do I care? No!)
+				conn.setAutoCommit(false);
 			}
 			catch ( Exception e ) {
 				Debug.err("Failed to connect to database. Error: " + e.getMessage());
@@ -46,6 +49,11 @@ public class Database {
 		return true;
 	}
 	
+	/**
+	 * SQL select query
+	 * @param sql sql select statement
+	 * @return data that you want
+	 */
 	public ResultSet query(String sql) {
 		ResultSet rs = null;
 		try {
@@ -59,6 +67,25 @@ public class Database {
 		return rs;
 	}
 	
+	/**
+	 * SQL insert / update
+	 * @param sql sql statement
+	 * @return number of rows affected
+	 */
+	public int update(String sql) {
+		int rowAffected = 0;
+		try {
+			Statement stmt = conn.createStatement();
+			rowAffected = stmt.executeUpdate(sql);
+			conn.commit();
+		} catch (SQLException e) {
+			Debug.err("Insertion/Update failed.");
+			e.printStackTrace();
+		}
+		
+		return rowAffected;
+	}
+	
 	public void close() {
 		try {
 			conn.close();

+ 25 - 1
src/me/blackphreak/Lib.java

@@ -1,7 +1,11 @@
 package me.blackphreak;
 
+import com.google.gson.Gson;
 import me.blackphreak.Debug.Debug;
 import me.blackphreak.Debug.DebugLevel;
+import me.blackphreak.Student.AbstractStudent;
+import me.blackphreak.Student.Types.LocalStudent;
+import me.blackphreak.Student.Types.OverseaStudent;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -12,9 +16,29 @@ public class Lib {
 			new InputStreamReader(System.in)
 	);
 	
+	public static AbstractStudent jsonToStudent(String json) {
+		try {
+			if (json.contains("HKID"))
+				return new Gson().fromJson(json, LocalStudent.class);
+			else if (json.contains("VISA"))
+				return new Gson().fromJson(json, OverseaStudent.class);
+			else
+				return null;  // unknown
+		}
+		catch (Exception ex) {
+			Debug.err(ex.getMessage());
+		}
+		
+		return null;
+	}
+	
 	public static String promptQuestion(String question) {
+		return promptQuestion(question, true);
+	}
+	
+	public static String promptQuestion(String question, boolean newLine) {
 		try {
-			System.out.print("\n" + question);
+			System.out.print((newLine ? "\n" : "") + question);
 			return reader.readLine();
 		} catch (IOException ex) {
 			Debug.err("Failed to prompt for question ["+question+"]");

+ 76 - 5
src/me/blackphreak/Main.java

@@ -29,6 +29,8 @@ package me.blackphreak;
 import me.blackphreak.CommandHandling.CommandHandler;
 import me.blackphreak.CommandHandling.CommandManager;
 import me.blackphreak.Debug.Debug;
+import me.blackphreak.Student.Types.LocalStudent;
+import me.blackphreak.Student.Types.OverseaStudent;
 
 import java.util.HashMap;
 
@@ -48,7 +50,11 @@ public class Main {
                 case "--save_config":
                     opts.put("save_config", args[++i]);
                     break;
-            
+                    
+                case "--create-student":
+                    opts.put("create-student", args[++i]);
+                    break;
+                    
                 case "-h":
                 case "--help":
                     printHelp();
@@ -61,6 +67,9 @@ public class Main {
             return;
         }*/
     
+        // uses to ignore next action(option) if false
+        boolean doNextAction = true;
+        
         // read & execute options with order:
         var config = (String) opts.getOrDefault("config", "");
         FileIO.readConfig(config);
@@ -74,11 +83,72 @@ public class Main {
             FileIO.saveConfig();
         }
         
-        // register commands
-        CommandManager.registerCommands();
+        var createStudent = opts.get("create-student");
+        if (createStudent != null) {
+            var stu = Lib.jsonToStudent((String)createStudent);
+            
+            // insert to database
+            var sb = new StringBuilder("INSERT INTO Student (StudentID, ChtName, EngName, Address, MobileNum, Nationality, ");
+            String sql;
+    
+            if (stu instanceof LocalStudent) {
+                var tmpStu = (LocalStudent)stu;
+                
+                //StudentID, ChtName, EngName, Address, MobileNum, Nationality, HKID
+                sb.append("HKID) VALUES (\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\");");
+        
+                sql = String.format(
+                        sb.toString(),
+                        tmpStu.getStudentID(),
+                        tmpStu.getChtName(),
+                        tmpStu.getEngName(),
+                        tmpStu.getHomeAddress(),
+                        tmpStu.getMobileNumber(),
+                        tmpStu.getNationality(),
+                        tmpStu.getHKID()
+                );
+            }
+            else if (stu instanceof OverseaStudent) {
+                var tmpStu = (OverseaStudent)stu;
+        
+                //StudentID, ChtName, EngName, Address, MobileNum, Nationality, VISA
+                sb.append("VISA) VALUES (\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\");");
         
-        // command mode (keep looping until exit)
-        CommandHandler.printOptions();
+                sql = String.format(
+                        sb.toString(),
+                        tmpStu.getStudentID(),
+                        tmpStu.getChtName() == null || tmpStu.getChtName().isEmpty()
+                                ? null
+                                : tmpStu.getChtName(),
+                        tmpStu.getEngName(),
+                        tmpStu.getHomeAddress(),
+                        tmpStu.getMobileNumber(),
+                        tmpStu.getNationality(),
+                        tmpStu.getVisa()
+                );
+            }
+            else
+            {
+                Debug.err("Unknown student type.");
+                sql = "";
+            }
+    
+            if (Database.getInstance().update(sql) > 0)
+                Debug.info("Insertion successful. Exiting...");
+            else
+                Debug.err("Failed to insert! Exiting...");
+            
+            doNextAction = false;
+        }
+        
+        if (doNextAction)
+        {
+            // register commands
+            CommandManager.registerCommands();
+            
+            // command mode (keep looping until exit)
+            CommandHandler.printOptions();
+        }
         
         // close the db connection
         db.close();
@@ -88,6 +158,7 @@ public class Main {
         System.out.println("----------- Help -----------");
         System.out.println("--config <file>          : Indicate program to use the specific configuration file\n");
         System.out.println("--save_config <file>     : Save the running configuration file.\n");
+        System.out.println("--create-student <json>  : Create a student by JSON string (for lazy people =w=).\n");
         System.out.println("-h  --help               : Print this help message\n\n");
     }
 }

+ 27 - 8
src/me/blackphreak/Student/AbstractStudent.java

@@ -1,5 +1,6 @@
 package me.blackphreak.Student;
 
+import me.blackphreak.Database;
 import me.blackphreak.Debug.Debug;
 
 import java.util.HashMap;
@@ -47,8 +48,16 @@ public abstract class AbstractStudent {
 	 * @return assign success / fail
 	 */
 	public boolean allocateStudentID() {
-		// TODO: get last studentID and then +1
-		String id = "";
+		// get last studentID and then +1
+		String id = null;
+		try {
+			id = (Database.getInstance()
+					.query("SELECT MAX(StudentID) FROM Student;")  // query last sID
+					.getInt(1) + 1)  // read & increasement
+					+ "";  // toString
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
 		return updateStudentID(id);
 	}
 	
@@ -58,20 +67,30 @@ public abstract class AbstractStudent {
 	 * @return false when duplicated
 	 */
 	public boolean updateStudentID(String newStudentID) {
-		// TODO: check for duplicate
-		if (true) {
-			// if duplicated
-			Debug.err("Failed to update studentID ["+getStudentID()+"] to ["+newStudentID+"]");
+		// check for duplicate
+		int numOfRowsFound;
+		try {
+			numOfRowsFound = Database.getInstance().query("SELECT COUNT(*) FROM Student WHERE studentID = \"" + newStudentID + "\";").getInt(1);
+		} catch (Exception e) {
+			e.printStackTrace();
 			return false;
 		}
 		
-		// no duplicate
-		if (true) {
+		if (numOfRowsFound > 0) {
+			// if duplicated
+			Debug.err("Failed to update studentID ["+getStudentID()+"] to ["+newStudentID+"]");
+			return false;
+		} else {
+			// no duplicate
 			this._studentID = newStudentID;
 		}
 		return true;
 	}
 	
+	public abstract String toJson();
+	
+	public abstract void promptQuestionEx();
+	
 	public abstract void printInfoExtra();
 	
 	// --- getter/setter ---

+ 18 - 3
src/me/blackphreak/Student/Types/LocalStudent.java

@@ -1,19 +1,23 @@
 package me.blackphreak.Student.Types;
 
+import com.google.gson.Gson;
 import me.blackphreak.Student.AbstractStudent;
 
-import static me.blackphreak.Lib.*;
+import static me.blackphreak.Lib.promptQuestion;
 
 public class LocalStudent extends AbstractStudent {
 	private String _HKID;
 	
+	public LocalStudent() {}
+	
 	public LocalStudent(String HKID) {
 		this._HKID = HKID;
 	}
 	
-	public LocalStudent() {
+	@Override
+	public void promptQuestionEx() {
 		do {
-			var inp = promptQuestion("    HKID: ")
+			var inp = promptQuestion("    HKID: ", false)
 					.toUpperCase();
 			
 			// HKID regex pattern src: https://www.regextester.com/97968
@@ -34,6 +38,17 @@ public class LocalStudent extends AbstractStudent {
 		this._HKID = HKID;
 	}
 	
+	@Override
+	public String toJson() {
+		try {
+			return new Gson().toJson(this, LocalStudent.class);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		
+		return "";
+	}
+	
 	@Override
 	public void printInfoExtra() {
 		System.out.println(String.format("    %13s: %s", "HKID", getHKID()));

+ 18 - 3
src/me/blackphreak/Student/Types/OverseaStudent.java

@@ -1,19 +1,23 @@
 package me.blackphreak.Student.Types;
 
+import com.google.gson.Gson;
 import me.blackphreak.Student.AbstractStudent;
 
 import static me.blackphreak.Lib.promptQuestion;
 
 public class OverseaStudent extends AbstractStudent {
-	private String _visa;  // null-able
+	private String _visa;
+	
+	public OverseaStudent() {}
 	
 	public OverseaStudent(String visa) {
 		this._visa = visa;
 	}
 	
-	public OverseaStudent() {
+	@Override
+	public void promptQuestionEx() {
 		do {
-			var inp = promptQuestion("    VISA: ")
+			var inp = promptQuestion("    VISA: ", false)
 					.toUpperCase();
 			
 			if (inp.isEmpty()
@@ -32,6 +36,17 @@ public class OverseaStudent extends AbstractStudent {
 		this._visa = visa;
 	}
 	
+	@Override
+	public String toJson() {
+		try {
+			return new Gson().toJson(this, OverseaStudent.class);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		
+		return "";
+	}
+	
 	@Override
 	public void printInfoExtra() {
 		System.out.println(String.format("    %13s: %s", "Visa", getVisa()));

BIN
students.db