Skip to content

Style/wrapper #65

Merged
merged 9 commits into from
Apr 16, 2026
140 changes: 87 additions & 53 deletions src/main/java/edu/group5/app/model/wrapper/DbWrapper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.group5.app.model.wrapper;

import edu.group5.app.utils.ParameterValidator;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
Expand All @@ -8,13 +9,13 @@
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* A class for wrapping the database.
*/
public class DbWrapper {
protected Connection connection;
private static final String CONNECTION_TYPE = "jdbc:h2:";
Expand All @@ -24,6 +25,11 @@ public class DbWrapper {
private List<Object[]> donations;
private Logger logger = Logger.getLogger(DbWrapper.class.getName());

/**
* The constructor, which constructs a String for connecting to the database.
*
* @param test Whether to construct the connection String for testing (in-memory) or not.
*/
public DbWrapper(boolean test) {
if (test) {
this.connectionString = CONNECTION_TYPE + "mem:test;" + DB_SCRIPT + "test_init.sql'";
Expand All @@ -33,6 +39,11 @@ public DbWrapper(boolean test) {
this.logger.info("connectionString constructed");
}

/**
* Connects to the database, and returns the result, logging failures.
*
* @return True if successful, false if not.
*/
public boolean connect() {
try {
this.connection = DriverManager.getConnection(this.connectionString);
Expand All @@ -49,8 +60,14 @@ public boolean connect() {
}
}

/**
* Disconnects the database connection, logging failures.
*
* @return True if successful, false if not.
*/
public boolean disconnect() {
try{ this.connection.close(); } catch (Exception e) {};
// We are not interested in whether it fails to close, as we check its closed status later.
try { this.connection.close(); } catch (Exception e) {};
try {
return this.connection.isClosed();
} catch (Exception e) {
Expand All @@ -59,16 +76,29 @@ public boolean disconnect() {
}
}

/**
* Closes queries and results.
*
* @param results The ResultSet to close, can be null.
* @param ps The PreparedStatement to close, can be null.
*/
private void close(ResultSet results, PreparedStatement ps) {
// This method can take null arguments, so an exception is expected.
try { results.close(); } catch (Exception e) {}
try { ps.close(); } catch (Exception e) {}
this.logger.info("results and ps closed");
}

/**
* Gets all users from the database.
*
* @return The users from the database returned as a List of Object arrays, where each Object
* array represents a user and a row in the users table in the database.
*/
public List<Object[]> importUsers() {
PreparedStatement ps = null;
ResultSet results = null;
try{
try {
ps = this.connection.prepareStatement("SELECT * FROM users");
results = ps.executeQuery();
List<Object[]> data = new ArrayList<Object[]>();
Expand All @@ -93,37 +123,32 @@ public List<Object[]> importUsers() {
return this.users;
}

public int exportUsers(List<Object[]> data) {
/**
* Puts new users into the database.
*
* @param data The new users to put into the database. Each Object array in the List is a new
* user to add as a row.
* @return The number of rows affected in the transaction.
* @throws IllegalArgumentException This exception is thrown when data is null, its rows are not
* of length 6, any of the rows are null, any of the rows are duplicates or existing rows in
* the database, or any of the values in the rows can't be cast to the correct data-types.
*/
public int exportUsers(List<Object[]> data) throws IllegalArgumentException {
this.importUsers();

if (data == null) {
throw new IllegalArgumentException("data can't be null");
}
if (data.isEmpty()) {
return 0;
}
if (data.get(0).length != 6) {
throw new IllegalArgumentException("data's arrays must have a length of 6");
}
if (data.stream().anyMatch(i -> Arrays.asList(i).contains(null))) {
throw new IllegalArgumentException("One or more rows in data contains null values");
}
if (this.users.size() > 0) {
if ((int) data.getLast()[0] <= (int) this.users.getLast()[0]) {
throw new IllegalArgumentException("data can't contain existing rows");
}
}
Set<Object> ids = new HashSet<>();
if (data.stream().anyMatch(i -> !ids.add(i[0]))) {
throw new IllegalArgumentException("data can't contain duplicate rows");
}


ParameterValidator.exportChecker(data, "data", this.users, 6);

PreparedStatement ps = null;
int rowsAffected = 0;
try {
ps = this.connection.prepareStatement(
"INSERT INTO users (user_id, role, first_name, last_name, email, password_hash) VALUES (?, ?, ?, ?, ?, ?)");
"""
INSERT INTO users
(user_id, role, first_name, last_name, email, password_hash)
VALUES
(?, ?, ?, ?, ?, ?)
"""
);
for (Object[] row : data) {
try {
ps.setInt(1, (int) row[0]);
Expand All @@ -144,18 +169,29 @@ public int exportUsers(List<Object[]> data) {
return rowsAffected;
}

/**
* Imports all donations.
*
* @return A List of Object arrays for each donation in the database.
*/
public List<Object[]> fetchAllDonations() {
return this.importDonations(0, true);
}

/**
* Imports the donations of a specific user based on a given user_id.
*
* @param user_id The id of the user to get the donations of.
* @return A List of Object arrays for each donation in the database.
*/
public List<Object[]> importDonations(int user_id) {
return this.importDonations(user_id, false);
}

private List<Object[]> importDonations(int user_id, boolean all) {
PreparedStatement ps = null;
ResultSet results = null;
try{
try {
if (all) {
ps = this.connection.prepareStatement("SELECT * FROM donations");
} else {
Expand Down Expand Up @@ -185,34 +221,32 @@ private List<Object[]> importDonations(int user_id, boolean all) {
return this.donations;
}

public int exportDonations(List<Object[]> data) {
/**
* Puts new donations into the database.
*
* @param data The new donation to put into the database. Each Object array in the List is a new
* donations to add as a row.
* @return The number of rows affected in the transaction.
* @throws IllegalArgumentException This exception is thrown when data is null, its rows are not
* of length 6, any of the rows are null, any of the rows are duplicates or existing rows in
* the database, or any of the values in the rows can't be cast to the correct data-types.
*/
public int exportDonations(List<Object[]> data) throws IllegalArgumentException {
this.fetchAllDonations();

if (data == null) {
throw new IllegalArgumentException("data can't be null");
}
if (data.isEmpty()) {
return 0;
}
if (data.get(0).length != 6) {
throw new IllegalArgumentException("data's arrays must have a length of 6");
}
if (data.stream().anyMatch(i -> Arrays.asList(i).contains(null))) {
throw new IllegalArgumentException("One or more rows in data contains null values");
}
if (this.donations.size() > 0 && (int) data.getLast()[0] <= (int) this.donations.getLast()[0]) {
throw new IllegalArgumentException("data can't contain existing rows");
}
Set<Object> ids = new HashSet<>();
if (data.stream().anyMatch(i -> !ids.add(i[0]))) {
throw new IllegalArgumentException("data can't contain duplicate rows");
}

ParameterValidator.exportChecker(data, "data", this.donations, 6);

PreparedStatement ps = null;
int rowsAffected = 0;
try {
ps = this.connection.prepareStatement(
"INSERT INTO donations (donation_id, user_id, organization_id, amount, dating, payment_method) VALUES (?, (SELECT user_id FROM users WHERE user_id = ?), ?, ?, ?, ?)");
"""
INSERT INTO donations
(donation_id, user_id, organization_id, amount, dating, payment_method)
VALUES
(?, (SELECT user_id FROM users WHERE user_id = ?), ?, ?, ?, ?)
"""
);
for (Object[] row : data) {
try {
for (int i = 0; i < 3; i++) {
Expand Down
7 changes: 2 additions & 5 deletions src/main/java/edu/group5/app/model/wrapper/OrgApiWrapper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.group5.app.model.wrapper;

import edu.group5.app.utils.ParameterValidator;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
Expand All @@ -24,11 +25,7 @@ public class OrgApiWrapper extends Wrapper {
* @param urlString A string of the URL that's being connected to.
*/
public OrgApiWrapper(String urlString) {
if (urlString == null) {
throw new IllegalArgumentException("url can't be null");
} else if (urlString.isBlank()) {
throw new IllegalArgumentException("url can't be blank");
}
ParameterValidator.stringChecker(urlString, "url");
try {
URI uri = URI.create(urlString);
this.client = HttpClient.newHttpClient();
Expand Down
78 changes: 67 additions & 11 deletions src/main/java/edu/group5/app/utils/ParameterValidator.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package edu.group5.app.utils;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* ParameterValidator is a utility class that provides static methods for validating various types of parameters.
* It includes methods for checking strings, integers, objects, and BigDecimal values to ensure they meet specific
* criteria such as not being null, not being blank, or being positive.
*
* ParameterValidator is a utility class that provides static methods for validating various types
* of parameters.
* It includes methods for checking strings, integers, objects, and BigDecimal values to ensure
* they meet specific criteria such as not being null, not being blank, or being positive.
*/
public final class ParameterValidator {

/**
* Validates that a string parameter is not null and not blank.
*
* @param stringArg the string parameter to validate
* @param variableName the name of the variable being validated, used in exception messages
* @throws IllegalArgumentException if the string is null or blank
*/
public static final void stringChecker(String stringArg, String variableName) throws IllegalArgumentException {
public static final void stringChecker(String stringArg, String variableName)
throws IllegalArgumentException {
nullCheck(stringArg, variableName);
if (stringArg.isBlank()) {
throw new IllegalArgumentException(String.format("%s can't be blank", variableName));
Expand All @@ -24,48 +31,97 @@ public static final void stringChecker(String stringArg, String variableName) th

/**
* Validates that an integer parameter is not null and is a positive integer.
*
* @param intArg the integer parameter to validate
* @param variableName the name of the variable being validated, used in exception messages
* @throws IllegalArgumentException if the integer is null or not a positive integer
*/
public static final void intChecker(int intArg, String variableName) throws IllegalArgumentException {
public static final void intChecker(int intArg, String variableName)
throws IllegalArgumentException {
if (intArg <= 0) {
throw new IllegalArgumentException(String.format("%s must be a positive integer", variableName));
throw new IllegalArgumentException(
String.format("%s must be a positive integer", variableName)
);
}
}

/**
* Validates that an object parameter is not null.
*
* @param objectArg the object parameter to validate
* @param variableName the name of the variable being validated, used in exception messages
* @throws IllegalArgumentException if the object is null
*/
public static final void objectChecker(Object objectArg, String variableName) throws IllegalArgumentException {
public static final void objectChecker(Object objectArg, String variableName)
throws IllegalArgumentException {
nullCheck(objectArg, variableName);
}

/**
* Validates that a BigDecimal parameter is not null and is greater than zero.
*
* @param bigDecimalArg the BigDecimal parameter to validate
* @param variableName the name of the variable being validated, used in exception messages
* @throws IllegalArgumentException if the BigDecimal is null or not greater than zero
*/
public static final void bigDecimalChecker(BigDecimal bigDecimalArg, String variableName) throws IllegalArgumentException {
public static final void bigDecimalChecker(BigDecimal bigDecimalArg, String variableName)
throws IllegalArgumentException {
nullCheck(bigDecimalArg, variableName);
if (bigDecimalArg.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException(String.format("%s must be larger than 0", variableName));
}
}

/**
* Helper method to check if a variable is null and throw an IllegalArgumentException with a formatted message if it is.
* Helper method to check if a variable is null and throw an IllegalArgumentException with a
* formatted message if it is.
*
* @param variable the variable to check for null
* @param variableName the name of the variable being checked, used in the exception message
* @throws IllegalArgumentException if the variable is null
*/
private static final void nullCheck(Object variable, String variableName) throws IllegalArgumentException {
private static final void nullCheck(Object variable, String variableName)
throws IllegalArgumentException {
if (variable == null) {
throw new IllegalArgumentException(String.format("%s can't be null", variableName));
}
}

/**
* A method for checking if the data to be exported is valid.
*
* @param data The data to export.
* @param dataName The name of the variable of the data to export.
* @param oldData The existing data to compare to for checking row existence.
* @throws IllegalArgumentException This exception is thrown when data is null, its rows are not
* of length 6, any of the rows are null, any of the rows are duplicates or existing rows in
* the database, or any of the values in the rows can't be cast to the correct data-types.
*/
public static final void exportChecker(
List<Object[]> data, String dataName, List<Object[]> oldData, int rowLength
) throws IllegalArgumentException {
objectChecker(data, dataName);
if (!data.isEmpty()) {
if (data.stream().anyMatch(i -> i.length != rowLength)) {
throw new IllegalArgumentException(
String.format("%s's arrays must have a length of 6", dataName)
);
}
if (data.stream().anyMatch(i -> Arrays.asList(i).contains(null))) {
throw new IllegalArgumentException(
String.format("One or more rows in %s contains null values", dataName)
);
}

Set<Object> ids = new HashSet<>();
if (data.stream().anyMatch(i -> !ids.add(i[0]))) {
throw new IllegalArgumentException("data can't contain duplicate rows");
}
if (oldData.size() > 0) {
if (oldData.stream().anyMatch(i -> !ids.add(i[0]))) {
throw new IllegalArgumentException("data can't contain existing rows");
}
}
}
}
}
Loading