Skip to content

Commit 24ab4c6

Browse files
committed
Building java V2.0
1 parent ff69965 commit 24ab4c6

16 files changed

Lines changed: 2434 additions & 0 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<groupId>com.studentgui</groupId>
7+
<artifactId>student-gui-java-v2</artifactId>
8+
<version>2.0-SNAPSHOT</version>
9+
10+
<properties>
11+
<maven.compiler.source>21</maven.compiler.source>
12+
<maven.compiler.target>21</maven.compiler.target>
13+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
14+
</properties>
15+
16+
<dependencies>
17+
<!-- MySQL Connector -->
18+
<dependency>
19+
<groupId>mysql</groupId>
20+
<artifactId>mysql-connector-java</artifactId>
21+
<version>8.0.33</version>
22+
</dependency>
23+
24+
<!-- Dotenv for env vars -->
25+
<dependency>
26+
<groupId>io.github.cdimascio</groupId>
27+
<artifactId>dotenv-java</artifactId>
28+
<version>3.0.0</version>
29+
</dependency>
30+
31+
<!-- FlatLaf for modern UI (includes both Dark and Light) -->
32+
<dependency>
33+
<groupId>com.formdev</groupId>
34+
<artifactId>flatlaf</artifactId>
35+
<version>3.4</version>
36+
</dependency>
37+
38+
<!-- JFreeChart for charts -->
39+
<dependency>
40+
<groupId>org.jfree</groupId>
41+
<artifactId>jfreechart</artifactId>
42+
<version>1.5.4</version>
43+
</dependency>
44+
45+
<!-- Apache POI for Excel export (.xlsx) -->
46+
<dependency>
47+
<groupId>org.apache.poi</groupId>
48+
<artifactId>poi-ooxml</artifactId>
49+
<version>5.3.0</version>
50+
</dependency>
51+
52+
<!-- UUID Creator — provides UUID v7 (time-ordered) -->
53+
<dependency>
54+
<groupId>com.github.f4b6a3</groupId>
55+
<artifactId>uuid-creator</artifactId>
56+
<version>6.0.0</version>
57+
</dependency>
58+
59+
<!-- Apache Commons Text — for HTML sanitization -->
60+
<dependency>
61+
<groupId>org.apache.commons</groupId>
62+
<artifactId>commons-text</artifactId>
63+
<version>1.12.0</version>
64+
</dependency>
65+
</dependencies>
66+
67+
<build>
68+
<plugins>
69+
<plugin>
70+
<groupId>org.apache.maven.plugins</groupId>
71+
<artifactId>maven-dependency-plugin</artifactId>
72+
<version>3.6.1</version>
73+
<executions>
74+
<execution>
75+
<id>copy-dependencies</id>
76+
<phase>package</phase>
77+
<goals>
78+
<goal>copy-dependencies</goal>
79+
</goals>
80+
<configuration>
81+
<outputDirectory>${project.build.directory}/lib</outputDirectory>
82+
</configuration>
83+
</execution>
84+
</executions>
85+
</plugin>
86+
</plugins>
87+
</build>
88+
</project>
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package com.studentgui.db;
2+
3+
import com.github.f4b6a3.uuid.UuidCreator;
4+
import io.github.cdimascio.dotenv.Dotenv;
5+
6+
import java.sql.*;
7+
import java.util.*;
8+
9+
public class DatabaseHelper {
10+
11+
private static final String[] SUBJECTS = {"Science", "Social", "Maths", "English", "Hindi", "Kannada"};
12+
private static final Map<String, Integer> SUBJ_MAP = new LinkedHashMap<>();
13+
static {
14+
SUBJ_MAP.put("Science", 101);
15+
SUBJ_MAP.put("Social", 102);
16+
SUBJ_MAP.put("Maths", 103);
17+
SUBJ_MAP.put("English", 104);
18+
SUBJ_MAP.put("Hindi", 105);
19+
SUBJ_MAP.put("Kannada", 106);
20+
}
21+
22+
private final String url;
23+
private final String user;
24+
private final String password;
25+
26+
public DatabaseHelper() {
27+
Dotenv dotenv = Dotenv.configure().ignoreIfMissing().load();
28+
String host = dotenv.get("DB_HOST", "localhost");
29+
String port = dotenv.get("DB_PORT", "3306");
30+
String db = dotenv.get("DB_NAME", "defaultdb");
31+
this.user = dotenv.get("DB_USER", "root");
32+
this.password = dotenv.get("DB_PASS", "");
33+
this.url = "jdbc:mysql://" + host + ":" + port + "/" + db + "?useSSL=true&serverTimezone=UTC";
34+
}
35+
36+
public Connection connect() throws SQLException {
37+
return DriverManager.getConnection(url, user, password);
38+
}
39+
40+
/**
41+
* Save student marks. Uses UUID v7 (time-ordered) for the MARKS.ID column.
42+
*/
43+
public void saveStudentMarks(String name, int rollNo, Map<String, Integer> marksMap) throws SQLException {
44+
try (Connection conn = connect()) {
45+
conn.setAutoCommit(false);
46+
try {
47+
// Upsert student
48+
try (PreparedStatement ps = conn.prepareStatement(
49+
"INSERT INTO STUDENTS (ROLL_NO, NAME) VALUES (?, ?) ON DUPLICATE KEY UPDATE NAME=?")) {
50+
ps.setInt(1, rollNo);
51+
ps.setString(2, name);
52+
ps.setString(3, name);
53+
ps.executeUpdate();
54+
}
55+
56+
// Upsert marks per subject
57+
for (Map.Entry<String, Integer> entry : marksMap.entrySet()) {
58+
Integer subjId = SUBJ_MAP.get(entry.getKey());
59+
if (subjId == null) continue;
60+
61+
// Delete old mark for this student/subject then insert fresh with UUID v7
62+
try (PreparedStatement del = conn.prepareStatement(
63+
"DELETE FROM MARKS WHERE ROLL_NO=? AND SUBJ_ID=?")) {
64+
del.setInt(1, rollNo);
65+
del.setInt(2, subjId);
66+
del.executeUpdate();
67+
}
68+
69+
// UUID v7 — time-ordered, sequential, globally unique
70+
String uuid7 = UuidCreator.getTimeOrderedWithRandom().toString();
71+
72+
try (PreparedStatement ins = conn.prepareStatement(
73+
"INSERT INTO MARKS (ID, ROLL_NO, SUBJ_ID, MARKS) VALUES (?, ?, ?, ?)")) {
74+
ins.setString(1, uuid7);
75+
ins.setInt(2, rollNo);
76+
ins.setInt(3, subjId);
77+
ins.setInt(4, entry.getValue());
78+
ins.executeUpdate();
79+
}
80+
}
81+
conn.commit();
82+
} catch (SQLException e) {
83+
conn.rollback();
84+
throw e;
85+
}
86+
}
87+
}
88+
89+
/**
90+
* Fetch all student records as a pivoted list of maps.
91+
* Returns null on connection failure.
92+
*/
93+
public List<Map<String, Object>> getAllRecords() throws SQLException {
94+
String query = """
95+
SELECT s.ROLL_NO, s.NAME,
96+
MAX(CASE WHEN m.SUBJ_ID = 101 THEN m.MARKS END) AS Science,
97+
MAX(CASE WHEN m.SUBJ_ID = 102 THEN m.MARKS END) AS Social,
98+
MAX(CASE WHEN m.SUBJ_ID = 103 THEN m.MARKS END) AS Maths,
99+
MAX(CASE WHEN m.SUBJ_ID = 104 THEN m.MARKS END) AS English,
100+
MAX(CASE WHEN m.SUBJ_ID = 105 THEN m.MARKS END) AS Hindi,
101+
MAX(CASE WHEN m.SUBJ_ID = 106 THEN m.MARKS END) AS Kannada
102+
FROM STUDENTS s
103+
LEFT JOIN MARKS m ON s.ROLL_NO = m.ROLL_NO
104+
GROUP BY s.ROLL_NO, s.NAME
105+
ORDER BY s.ROLL_NO
106+
""";
107+
108+
List<Map<String, Object>> results = new ArrayList<>();
109+
try (Connection conn = connect();
110+
PreparedStatement ps = conn.prepareStatement(query);
111+
ResultSet rs = ps.executeQuery()) {
112+
113+
ResultSetMetaData meta = rs.getMetaData();
114+
int cols = meta.getColumnCount();
115+
while (rs.next()) {
116+
Map<String, Object> row = new LinkedHashMap<>();
117+
for (int i = 1; i <= cols; i++) {
118+
row.put(meta.getColumnLabel(i), rs.getObject(i));
119+
}
120+
results.add(row);
121+
}
122+
}
123+
return results;
124+
}
125+
126+
public void deleteStudent(int rollNo) throws SQLException {
127+
try (Connection conn = connect()) {
128+
conn.setAutoCommit(false);
129+
try {
130+
try (PreparedStatement ps = conn.prepareStatement("DELETE FROM MARKS WHERE ROLL_NO=?")) {
131+
ps.setInt(1, rollNo);
132+
ps.executeUpdate();
133+
}
134+
try (PreparedStatement ps = conn.prepareStatement("DELETE FROM STUDENTS WHERE ROLL_NO=?")) {
135+
ps.setInt(1, rollNo);
136+
ps.executeUpdate();
137+
}
138+
conn.commit();
139+
} catch (SQLException e) {
140+
conn.rollback();
141+
throw e;
142+
}
143+
}
144+
}
145+
146+
public static String[] getSubjects() {
147+
return SUBJECTS;
148+
}
149+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package com.studentgui.util;
2+
3+
import org.apache.commons.text.StringEscapeUtils;
4+
import java.util.List;
5+
import java.util.regex.Pattern;
6+
7+
/**
8+
* InputValidator v2 — enhanced with SQL injection detection and HTML
9+
* sanitization.
10+
* Mirrors Python v2's input_validator.py logic.
11+
*/
12+
public class InputValidator {
13+
14+
// ── SQL injection patterns (ported from Python v2) ──────────────────────────
15+
private static final List<Pattern> SQL_INJECTION_PATTERNS = List.of(
16+
Pattern.compile(
17+
"(\\b(SELECT|INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER|CREATE|EXEC|EXECUTE|UNION|GRANT|REVOKE)\\b)",
18+
Pattern.CASE_INSENSITIVE),
19+
Pattern.compile("(-{2,})"), // SQL line comments
20+
Pattern.compile("(;)"), // Statement terminator
21+
Pattern.compile("(/\\*|\\*/)"), // Block comments
22+
Pattern.compile("(\\bOR\\b.*=.*)", Pattern.CASE_INSENSITIVE), // OR 1=1
23+
Pattern.compile("(\\bAND\\b.*=.*)", Pattern.CASE_INSENSITIVE), // AND 1=1
24+
Pattern.compile("('.*--)"), // Quote + comment
25+
Pattern.compile("(\\bxp_\\w+)", Pattern.CASE_INSENSITIVE), // Extended stored procs
26+
Pattern.compile("(\\bsp_\\w+)", Pattern.CASE_INSENSITIVE) // Stored procs
27+
);
28+
29+
public record ValidationResult(boolean isValid, String errorMessage, Object parsedValue) {
30+
public static ValidationResult ok(Object value) {
31+
return new ValidationResult(true, "", value);
32+
}
33+
34+
public static ValidationResult err(String msg) {
35+
return new ValidationResult(false, msg, null);
36+
}
37+
}
38+
39+
// ── SQL injection check ──────────────────────────────────────────────────────
40+
public static boolean containsSqlInjection(String value) {
41+
if (value == null || value.isEmpty())
42+
return false;
43+
for (Pattern p : SQL_INJECTION_PATTERNS) {
44+
if (p.matcher(value).find())
45+
return true;
46+
}
47+
return false;
48+
}
49+
50+
// ── Sanitize: strip null bytes, trim, HTML-escape ────────────────────────────
51+
public static String sanitize(String value) {
52+
if (value == null)
53+
return "";
54+
value = value.replace("\0", "").strip();
55+
return StringEscapeUtils.escapeHtml4(value);
56+
}
57+
58+
// ── Name validation ──────────────────────────────────────────────────────────
59+
public static ValidationResult validateName(String name) {
60+
if (name == null || name.isBlank())
61+
return ValidationResult.err("Name cannot be empty");
62+
name = name.strip();
63+
if (name.length() < 2)
64+
return ValidationResult.err("Name must be at least 2 characters");
65+
if (name.length() > 100)
66+
return ValidationResult.err("Name cannot exceed 100 characters");
67+
if (containsSqlInjection(name))
68+
return ValidationResult.err("Invalid characters detected in name");
69+
if (!name.matches("^[a-zA-Z\\s]+$"))
70+
return ValidationResult.err("Name can only contain letters and spaces");
71+
return ValidationResult.ok(sanitize(name));
72+
}
73+
74+
// ── Roll number validation ───────────────────────────────────────────────────
75+
public static ValidationResult validateRollNumber(String roll) {
76+
if (roll == null || roll.isBlank())
77+
return ValidationResult.err("Roll Number cannot be empty");
78+
roll = roll.strip();
79+
if (containsSqlInjection(roll))
80+
return ValidationResult.err("Invalid characters in Roll Number");
81+
try {
82+
int value = Integer.parseInt(roll);
83+
if (value <= 0)
84+
return ValidationResult.err("Roll Number must be positive");
85+
if (value > 999999)
86+
return ValidationResult.err("Roll Number cannot exceed 999999");
87+
return ValidationResult.ok(value);
88+
} catch (NumberFormatException e) {
89+
return ValidationResult.err("Roll Number must be a valid integer");
90+
}
91+
}
92+
93+
// ── Marks validation ─────────────────────────────────────────────────────────
94+
public static ValidationResult validateMarks(String marks, String subjectName) {
95+
if (marks == null || marks.isBlank())
96+
return ValidationResult.ok(null); // optional
97+
marks = marks.strip();
98+
if (containsSqlInjection(marks))
99+
return ValidationResult.err("Invalid characters in " + subjectName + " marks");
100+
try {
101+
int value = Integer.parseInt(marks);
102+
if (value < 0)
103+
return ValidationResult.err(subjectName + " marks cannot be negative");
104+
if (value > 100)
105+
return ValidationResult.err(subjectName + " marks cannot exceed 100");
106+
return ValidationResult.ok(value);
107+
} catch (NumberFormatException e) {
108+
return ValidationResult.err("Marks for " + subjectName + " must be a valid integer");
109+
}
110+
}
111+
112+
// ── Search term validation ───────────────────────────────────────────────────
113+
public static ValidationResult validateSearchTerm(String term) {
114+
if (term == null || term.isBlank())
115+
return ValidationResult.ok(null);
116+
if (containsSqlInjection(term))
117+
return ValidationResult.err("Invalid characters in search term");
118+
if (term.length() > 100)
119+
return ValidationResult.err("Search term is too long");
120+
return ValidationResult.ok(term);
121+
}
122+
}

0 commit comments

Comments
 (0)